tracing: probeevent: Improve fetcharg features
Subsystems (4)
Masami Hiramatsu
maintainercover tracing: probeevent: Improve fetcharg features
Hi,
This series improves probeevent variable fetcharg framework,
- Rewrite fetcharg framework with fetch_insn, switch-case based instead of function pointer. (maybe good for retpoline)
- Add "symbol" type support, which shows symbol+offset instead of address value.
- Add "$argN" fetcharg, which fetches function parameters. (currently only for x86-64)
- Add array type support for basic types (except for string), which enables to get fixed length array from probeevents.
All ideas came from Steve's function event tracer. Thanks Steve!
I'll add string (pointer) array support and testcases for these features in the next version.
Here are examples:
o 'symbol' type
# echo 'p vfs_read $stack0:symbol' > kprobe_events
# echo 1 > events/kprobes/p_vfs_read_0/enable
# tail -n 3 trace
sh-729 [007] ...2 105.753637: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=SyS_read+0x42/0x90
tail-736 [000] ...2 105.754904: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=kernel_read+0x2c/0x40
tail-736 [000] ...2 105.754929: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=kernel_read+0x2c/0x40o $argN
# echo 'p vfs_read $arg0 $arg1 $arg2' > kprobe_events
# echo 1 > events/kprobes/p_vfs_read_0/enable
# tail -n 3 trace
sh-726 [007] ...2 134.288973: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffff88001d98ec00 arg2=0x7ffeb4330f79 arg3=0x1
tail-731 [000] ...2 134.289987: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffff88001d9dd200 arg2=0xffff88001d8a0a00 arg3=0x80
tail-731 [000] ...2 134.290016: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffff88001d9dd200 arg2=0xffff88001faf4a00 arg3=0x150o Array type
# echo 'p vfs_read +0($stack):x64 +0($stack):x8[8]' > kprobe_events
# echo 1 > events/kprobes/p_vfs_read_0/enable
# tail -n 3 trace
sh-729 [007] ...2 91.701664: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffffffff811b1252 arg2={0x52,0x12,0x1b,0x81,0xff,0xff,0xff,0xff}
tail-734 [000] ...2 91.702366: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffffffff811b0dec arg2={0xec,0xd,0x1b,0x81,0xff,0xff,0xff,0xff}
tail-734 [000] ...2 91.702386: p_vfs_read_0: (vfs_read+0x0/0x130) arg1=0xffffffff811b0dec arg2={0xec,0xd,0x1b,0x81,0xff,0xff,0xff,0xff}
#
# cat events/kprobes/p_vfs_read_0/format
name: p_vfs_read_0
ID: 1069
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;field:unsigned long __probe_ip; offset:8; size:8; signed:0; field:u64 arg1; offset:16; size:0; signed:0; field:u8 arg2[8]; offset:24; size:8; signed:0;
print fmt: "(%lx) arg1=0x%Lx arg2={0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x}", REC->__probe_ip, REC->arg1, REC->arg2[0], REC->arg2[1], REC->arg2[2], REC->arg2[3], REC->arg2[4], REC->arg2[5], REC->arg2[6], REC->arg2[7]
Thanks,
---
Masami Hiramatsu (8):
tracing: probeevent: Cleanup print argument functions
tracing: probeevent: Cleanup argument field definition
tracing: probeevent: Remove NOKPROBE_SYMBOL from print functions
tracing: probeevent: Introduce new argument fetching code
tracing: probeevent: Add symbol type
x86: ptrace: Add function argument access API
tracing: probeevent: Add $argN for accessing function args
tracing: probeevent: Add an array for basic types
Documentation/trace/kprobetrace.txt | 20 + arch/Kconfig | 7 arch/x86/Kconfig | 1 arch/x86/include/asm/ptrace.h | 38 ++ kernel/trace/trace_kprobe.c | 396 +++++++++-------------- kernel/trace/trace_probe.c | 614 ++++++++++++++++------------------- kernel/trace/trace_probe.h | 313 +++++++++--------- kernel/trace/trace_uprobe.c | 188 ++++++----- 8 files changed, 771 insertions(+), 806 deletions(-)
--
Masami Hiramatsu (Linaro) <[email protected]>
Masami Hiramatsu
maintainerRFC PATCH 1/8 tracing: probeevent: Cleanup print argument functions
Current print argument functions prints the argument name too. It is not good for printing out multiple values for one argument. This change it to just print out the value.
--- kernel/trace/trace_kprobe.c | 20 ++++++-------------- kernel/trace/trace_probe.c | 12 +++++------- kernel/trace/trace_probe.h | 19 ++++++++++++++++--- kernel/trace/trace_uprobe.c | 9 ++------- 4 files changed, 29 insertions(+), 31 deletions(-)
kernel/trace/trace_kprobe.c +6 -14
@@ -1058,8 +1058,6 @@ print_kprobe_event(struct trace_iterator *iter, int flags,struct kprobe_trace_entry_head *field;struct trace_seq *s = &iter->seq;struct trace_probe *tp; -u8 *data; -int i; field = (struct kprobe_trace_entry_head *)iter->ent; tp = container_of(event, struct trace_probe, call.event);
@@ -1071,11 +1069,9 @@ print_kprobe_event(struct trace_iterator *iter, int flags,trace_seq_putc (s,')' ); - data = (u8 *)&field[1 ]; -for (i =0 ; i < tp->nr_args; i++) -if (!tp->args[i].type->if (print_probe_args (s, tp->args, tp->nr_args, + (u8 *)&field[1 ], field) <0 ) + goto out;trace_seq_putc (s,'\n' ); out:
@@ -1089,8 +1085,6 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,struct kretprobe_trace_entry_head *field;struct trace_seq *s = &iter->seq;struct trace_probe *tp; -u8 *data; -int i; field = (struct kretprobe_trace_entry_head *)iter->ent; tp = container_of(event, struct trace_probe, call.event);
@@ -1107,11 +1101,9 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,trace_seq_putc (s,')' ); - data = (u8 *)&field[1 ]; -for (i =0 ; i < tp->nr_args; i++) -if (!tp->args[i].type->if (print_probe_args (s, tp->args, tp->nr_args, + (u8 *)&field[1 ], field) <0 ) + goto out;trace_seq_putc (s,'\n' );
kernel/trace/trace_probe.c +5 -7
@@ -38,10 +38,9 @@ const char *reserved_field_names[] = {/* Printing in basic type function template */ #define DEFINE_BASIC_PRINT_TYPE_FUNC (tname, type, fmt) \ -int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, const char *name, \ - void *data, void *ent) \ +int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\ { \ - trace_seq_printf(s, " %s=" fmt, name, *(type *)data); \ + trace_seq_printf(s, fmt, *(type *)data); \ return !trace_seq_has_overflowed(s); \ } \ const char PRINT_TYPE_FMT_NAME(tname)[] = fmt; \
@@ -61,15 +60,14 @@ DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC (x64, u64,"0x%Lx" ) /* Print type function for string type */ -int PRINT_TYPE_FUNC_NAME (string )(struct trace_seq *s,const char *name, -void *data,void *ent) +int PRINT_TYPE_FUNC_NAME (string )(struct trace_seq *s,void *data,void *ent) {int len = *(u32 *)data >>16 ;if (!len) -trace_seq_printf (s," %s=(fault)" , name); +trace_seq_puts (s,"(fault)" );else -trace_seq_printf (s," %s=\"%s\"" , name, +trace_seq_printf (s,"\"%s\"" , (const char *)get_loc_data (data, ent));return !trace_seq_has_overflowed (s); }
kernel/trace/trace_probe.h +16 -3
@@ -94,7 +94,7 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)/* Data fetch function type */ typedef void (*fetch_func_t )(struct pt_regs *,void *,void *);/* Printing function type */ -typedef int (*print_type_func_t )(struct trace_seq *,const char *,void *,void *); +typedef int (*print_type_func_t )(struct trace_seq *,void *,void *);/* Fetch types */ enum {
@@ -136,8 +136,7 @@ typedef u32 string_size;/* Printing in basic type function template */ #define DECLARE_BASIC_PRINT_TYPE_FUNC (type) \ -int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name, \ - void *data, void *ent); \ +int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, void *data, void *ent);\ extern const char PRINT_TYPE_FMT_NAME(type)[]DECLARE_BASIC_PRINT_TYPE_FUNC (u8);
@@ -403,4 +402,18 @@ store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs, } } +static inline int +print_probe_args (struct trace_seq *s,struct probe_arg *args,int nr_args, +u8 *data,void *field) +{ +int i; + +for (i =0 ; i < nr_args; i++) { +trace_seq_printf (s," %s=" , args[i].name); +if (!args[i].type->return -ENOMEM ; + } +return 0 ; +} +extern int set_print_fmt (struct trace_probe *tp,bool is_return);
kernel/trace/trace_uprobe.c +2 -7
@@ -864,7 +864,6 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *estruct trace_seq *s = &iter->seq;struct trace_uprobe *tu;u8 *data; -int i; entry = (struct uprobe_trace_entry_head *)iter->ent; tu = container_of(event, struct trace_uprobe, tp.call.event);
@@ -881,12 +880,8 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *e data =DATAOF_TRACE_ENTRY (entry, false); } -for (i =0 ; i < tu->tp.nr_args; i++) { -struct probe_arg *parg = &tu->tp.args[i]; - -if (!parg->type->if (print_probe_args (s, tu->tp.args, tu->tp.nr_args, data, entry) <0 ) + goto out;trace_seq_putc (s,'\n' );
Masami Hiramatsu
maintainerRFC PATCH 2/8 tracing: probeevent: Cleanup argument field definition
In reply to Masami HiramatsuCleanup event argument definition code in one place for maintenancability.
--- kernel/trace/trace_kprobe.c | 32 ++++---------------------------- kernel/trace/trace_probe.c | 21 +++++++++++++++++++++ kernel/trace/trace_probe.h | 2 ++ kernel/trace/trace_uprobe.c | 15 ++------------- 4 files changed, 29 insertions(+), 41 deletions(-)
kernel/trace/trace_kprobe.c +4 -28
@@ -1114,49 +1114,25 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,static int kprobe_event_define_fields (struct trace_event_call *event_call) { -int ret, i; +int ret;struct kprobe_trace_entry_head field;struct trace_kprobe *tk = (struct trace_kprobe *)event_call->data; DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); -/* Set argument names as fields */ -for (i =0 ; i < tk->tp.nr_args; i++) { -struct probe_arg *parg = &tk->tp.args[i]; - ret =trace_define_field (event_call, parg->type->fmttype, - parg->name, -sizeof (field) + parg->offset, - parg->type->size, - parg->type->is_signed, -FILTER_OTHER ); -if (ret) -return ret; - } -return 0 ; +return traceprobe_define_arg_fields (event_call,sizeof (field), &tk->tp); }static int kretprobe_event_define_fields (struct trace_event_call *event_call) { -int ret, i; +int ret;struct kretprobe_trace_entry_head field;struct trace_kprobe *tk = (struct trace_kprobe *)event_call->data; DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0); DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0); -/* Set argument names as fields */ -for (i =0 ; i < tk->tp.nr_args; i++) { -struct probe_arg *parg = &tk->tp.args[i]; - ret =trace_define_field (event_call, parg->type->fmttype, - parg->name, -sizeof (field) + parg->offset, - parg->type->size, - parg->type->is_signed, -FILTER_OTHER ); -if (ret) -return ret; - } -return 0 ; +return traceprobe_define_arg_fields (event_call,sizeof (field), &tk->tp); }#ifdef CONFIG_PERF_EVENTS
kernel/trace/trace_probe.c +21 -0
@@ -682,3 +682,24 @@ int set_print_fmt(struct trace_probe *tp, bool is_return)return 0 ; } + +int traceprobe_define_arg_fields (struct trace_event_call *event_call, +size_t offset,struct trace_probe *tp) +{ +int ret, i; + +/* Set argument names as fields */ +for (i =0 ; i < tp->nr_args; i++) { +struct probe_arg *parg = &tp->args[i]; + + ret =trace_define_field (event_call, parg->type->fmttype, + parg->name, + offset + parg->offset, + parg->type->size, + parg->type->is_signed, +FILTER_OTHER ); +if (ret) +return ret; + } +return 0 ; +}
kernel/trace/trace_probe.h +2 -0
@@ -417,3 +417,5 @@ print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, }extern int set_print_fmt (struct trace_probe *tp,bool is_return); +extern int traceprobe_define_arg_fields (struct trace_event_call *event_call, +size_t offset,struct trace_probe *tp);
kernel/trace/trace_uprobe.c +2 -13
@@ -982,7 +982,7 @@ probe_event_disable(struct trace_uprobe *tu, struct trace_event_file *file)static int uprobe_event_define_fields (struct trace_event_call *event_call) { -int ret, i, size; +int ret, size;struct uprobe_trace_entry_head field;struct trace_uprobe *tu = event_call->data;
@@ -994,19 +994,8 @@ static int uprobe_event_define_fields(struct trace_event_call *event_call) DEFINE_FIELD(unsigned long, vaddr[0], FIELD_STRING_IP, 0); size =SIZEOF_TRACE_ENTRY (false); } -/* Set argument names as fields */ -for (i =0 ; i < tu->tp.nr_args; i++) { -struct probe_arg *parg = &tu->tp.args[i]; - - ret =trace_define_field (event_call, parg->type->fmttype, - parg->name, size + parg->offset, - parg->type->size, parg->type->is_signed, -FILTER_OTHER ); -if (ret) -return ret; - } -return 0 ; +return traceprobe_define_arg_fields (event_call, size, &tu->tp); }#ifdef CONFIG_PERF_EVENTS
Masami Hiramatsu
maintainerRFC PATCH 3/8 tracing: probeevent: Remove NOKPROBE_SYMBOL from print functions
In reply to Masami HiramatsuRemove unneeded NOKPROBE_SYMBOL from print functions since the print functions are only used when printing out the trace data, and not from kprobe handler.
--- kernel/trace/trace_probe.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
kernel/trace/trace_probe.c +1 -3
@@ -43,8 +43,7 @@ int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\trace_seq_printf (s, fmt, *(type *)data); \return !trace_seq_has_overflowed (s); \ } \ -const char PRINT_TYPE_FMT_NAME (tname )[] = fmt; \ -NOKPROBE_SYMBOL (PRINT_TYPE_FUNC_NAME (tname)); +const char PRINT_TYPE_FMT_NAME (tname )[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, u8, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC (u16, u16,"%u" )
@@ -71,7 +70,6 @@ int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent) (const char *)get_loc_data(data, ent));return !trace_seq_has_overflowed (s); } -NOKPROBE_SYMBOL (PRINT_TYPE_FUNC_NAME (string));const char PRINT_TYPE_FMT_NAME (string )[] ="\\\"%s\\\"" ;
Masami Hiramatsu
maintainerRFC PATCH 4/8 tracing: probeevent: Introduce new argument fetching code
In reply to Masami HiramatsuReplace {k,u}probe event argument fetching framework with switch-case based. Currently that is implemented with structures, macros and chain of function-pointers, which is more complicated than necessary and may get a performance penalty by retpoline.
This simplify that with an array of "fetch_insn" (opcode and oprands), and make process_fetch_insn() just interprets it. No function pointers are used.
--- kernel/trace/trace_kprobe.c | 313 ++++++++++++------------------ kernel/trace/trace_probe.c | 446 +++++++++++++++---------------------------- kernel/trace/trace_probe.h | 252 +++++++++++------------- kernel/trace/trace_uprobe.c | 149 ++++++++------ 4 files changed, 472 insertions(+), 688 deletions(-)
kernel/trace/trace_kprobe.c +129 -184
@@ -97,184 +97,6 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);static int kretprobe_dispatcher (struct kretprobe_instance *ri,struct pt_regs *regs); -/* Memory fetching by symbol */ -struct symbol_cache { -char *symbol; -long offset; -unsigned long addr; -}; - -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; -} - -void free_symbol_cache (struct symbol_cache *sc) -{ -kfree (sc->symbol); -kfree (sc); -} - -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; -} - -/* -* Kprobes-specific fetch functions -*/ -#define DEFINE_FETCH_stack (type) \ -static 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)); \ -} \ -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(stack, type)); - -DEFINE_BASIC_FETCH_FUNCS (stack) -/* No string on the stack entry */ -#define fetch_stack_string NULL -#define fetch_stack_string_size NULL - -#define DEFINE_FETCH_memory (type) \ -static 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; \ -} \ -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, type)); - -DEFINE_BASIC_FETCH_FUNCS (memory) -/* -* Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max -* length and relative data location. -*/ -static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, -void *addr,void *dest) -{ -int maxlen =get_rloc_len (*(u32 *)dest); -u8 *dst =get_rloc_data (dest); -long ret; - -if (!maxlen) -return ; - -/* -* Try to get string again, since the string can be changed while -* probing. -*/ - ret =strncpy_from_unsafe (dst, addr, maxlen); - -if (ret <0 ) {/* Failed to fetch string */ - dst[0 ] ='\0' ; - *(u32 *)dest =make_data_rloc (0 ,get_rloc_offs (*(u32 *)dest)); - }else { - *(u32 *)dest =make_data_rloc (ret,get_rloc_offs (*(u32 *)dest)); - } -} -NOKPROBE_SYMBOL (FETCH_FUNC_NAME (memory, string)); - -/* Return the length of string -- including null terminal byte */ -static 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; -} -NOKPROBE_SYMBOL (FETCH_FUNC_NAME (memory, string_size)); - -#define DEFINE_FETCH_symbol (type) \ -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; \ -} \ -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(symbol, type)); - -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 */ -static const struct fetch_type kprobes_fetch_type_table[] = { -/* 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 ), -ASSIGN_FETCH_TYPE_ALIAS (x8, u8, u8,0 ), -ASSIGN_FETCH_TYPE_ALIAS (x16, u16, u16,0 ), -ASSIGN_FETCH_TYPE_ALIAS (x32, u32, u32,0 ), -ASSIGN_FETCH_TYPE_ALIAS (x64, u64, u64,0 ), - -ASSIGN_FETCH_TYPE_END -}; -/* * Allocate new trace_probe and initialize it (including kprobes). */
@@ -458,14 +280,11 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)/* Internal register function - just handle k*probes and flags */ static int __register_trace_kprobe (struct trace_kprobe *tk) { -int i, ret; +int ret;if (trace_probe_is_registered (&tk->tp))return -EINVAL ; -for (i =0 ; i < tk->tp.nr_args; i++) -traceprobe_update_arg (&tk->tp.args[i]); -/* Set/clear disabled flag according to tp->flag */ if (trace_probe_is_enabled (&tk->tp)) tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED ;
@@ -798,8 +617,7 @@ static int create_trace_kprobe(int argc, char **argv)/* Parse fetch argument */ ret =traceprobe_parse_probe_arg (arg, &tk->tp.size, parg, - is_return, true, - kprobes_fetch_type_table); + is_return, true);if (ret) {pr_info ("Parse error at argument[%d]. (%d)\n" , i, ret); goto error;
@@ -953,6 +771,133 @@ static const struct file_operations kprobe_profile_ops = { .release = seq_release, }; +/* Kprobe specific fetch functions */ + +/* Return the length of string -- including null terminal byte */ +static nokprobe_inline void +fetch_store_strlen (unsigned long 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; +} + +/* +* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max +* length and relative data location. +*/ +static nokprobe_inline void +fetch_store_string (unsigned long addr,void *dest) +{ +int maxlen =get_rloc_len (*(u32 *)dest); +u8 *dst =get_rloc_data (dest); +long ret; + +if (!maxlen) +return ; + +/* +* Try to get string again, since the string can be changed while +* probing. +*/ + ret =strncpy_from_unsafe (dst, (void *)addr, maxlen); + +if (ret <0 ) {/* Failed to fetch string */ + dst[0 ] ='\0' ; + *(u32 *)dest =make_data_rloc (0 ,get_rloc_offs (*(u32 *)dest)); + }else { + *(u32 *)dest =make_data_rloc (ret,get_rloc_offs (*(u32 *)dest)); + } +} + +/* Note that we don't verify it, since the code does not come from user space */ +static int +process_fetch_insn (struct fetch_insn *code,struct pt_regs *regs,void *dest, +bool pre) +{ +unsigned long val; +int ret; + +/* 1st stage: get value from context */ +switch (code->op) { +case FETCH_OP_REG : + val =regs_get_register (regs, code->param); +break ; +case FETCH_OP_STACK : + val =regs_get_kernel_stack_nth (regs, code->param); +break ; +case FETCH_OP_STACKP : + val =kernel_stack_pointer (regs); +break ; +case FETCH_OP_RETVAL : + val =regs_return_value (regs); +break ; +case FETCH_OP_IMM : + val = code->immediate; +break ; +case FETCH_OP_COMM : + val = (unsigned long )current->comm; +break ; +default : +return -EILSEQ ; + } + code++; + +/* 2nd stage: dereference memory if needed */ +while (code->op ==FETCH_OP_DEREF ) { + ret =probe_kernel_read (&val, (void *)val + code->offset, +sizeof (val)); +if (ret) +return ret; + code++; + } + +/* 3rd stage: store value to buffer */ +switch (code->op) { +case FETCH_OP_ST_RAW : +fetch_store_raw (val, code, dest); +break ; +case FETCH_OP_ST_MEM : +probe_kernel_read (dest, (void *)val + code->offset, code->size); +break ; +case FETCH_OP_ST_STRING : +if (pre) +fetch_store_strlen (val + code->offset, dest); +else +fetch_store_string (val + code->offset, dest); +break ; +default : +return -EILSEQ ; + } + code++; + +/* 4th stage: modify stored value if needed */ +if (code->op ==FETCH_OP_MOD_BF ) { +fetch_apply_bitfield (code, dest); + code++; + } + +return code->op ==FETCH_OP_END ?0 : -EILSEQ ; +} +NOKPROBE_SYMBOL (process_fetch_insn) +/* Kprobe handler */ static nokprobe_inline void __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
kernel/trace/trace_probe.c +151 -295
613 lines not on this page · raw message
kernel/trace/trace_probe.h +109 -143
342 lines not on this page · raw message
kernel/trace/trace_uprobe.c +83 -66
187 lines not on this page · raw message
Masami Hiramatsu
maintainerRFC PATCH 5/8 tracing: probeevent: Add symbol type
In reply to Masami HiramatsuAdd "symbol" type to probeevent, which is an alias of u32 or u64 (depends on BITS_PER_LONG). This shows the result value in symbol+offset style. This type is only available with kprobe events.
--- Documentation/trace/kprobetrace.txt | 3 +++ kernel/trace/trace_probe.c | 8 ++++++++ kernel/trace/trace_probe.h | 12 +++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-)
Documentation/trace/kprobetrace.txt +3 -0
@@ -62,6 +62,7 @@ respectively. 'x' prefix implies it is unsigned. Traced arguments are shown in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32' or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and x86-64 uses x64). + String type is a special type, which fetches a "null-terminated" string from kernel space. This means it will fail and store NULL if the string container has been paged out.
@@ -70,6 +71,8 @@ offset, and container-size (usually 32). The syntax is; b<bit-width>@<bit-offset>/<container-size> +Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG) +which shows given pointer in "symbol+offset" style. For $comm, the default type is "string"; any other type is invalid.
kernel/trace/trace_probe.c +8 -0
@@ -58,6 +58,13 @@ DEFINE_BASIC_PRINT_TYPE_FUNC(x16, u16, "0x%x") DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x") DEFINE_BASIC_PRINT_TYPE_FUNC(x64, u64, "0x%Lx") +int PRINT_TYPE_FUNC_NAME (symbol )(struct trace_seq *s,void *data,void *ent) +{ +trace_seq_printf (s,"%pS" , (void *)*(unsigned long *)data); +return !trace_seq_has_overflowed (s); +} +const char PRINT_TYPE_FMT_NAME (symbol )[] ="%pS" ; +/* Print type function for string type */ int PRINT_TYPE_FUNC_NAME (string )(struct trace_seq *s,void *data,void *ent) {
@@ -103,6 +110,7 @@ static const struct fetch_type probe_fetch_types[] = {ASSIGN_FETCH_TYPE_ALIAS (x16, u16, u16,0 ),ASSIGN_FETCH_TYPE_ALIAS (x32, u32, u32,0 ),ASSIGN_FETCH_TYPE_ALIAS (x64, u64, u64,0 ), +ASSIGN_FETCH_TYPE_ALIAS (symbol,ADDR_FETCH_TYPE ,ADDR_FETCH_TYPE ,0 ), ASSIGN_FETCH_TYPE_END };
kernel/trace/trace_probe.h +9 -3
@@ -171,6 +171,7 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(x32);DECLARE_BASIC_PRINT_TYPE_FUNC (x64);DECLARE_BASIC_PRINT_TYPE_FUNC (string); +DECLARE_BASIC_PRINT_TYPE_FUNC (symbol);/* Default (unsigned long) fetch type */ #define __DEFAULT_FETCH_TYPE (t) x##t
@@ -178,6 +179,10 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(string);#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) +#define __ADDR_FETCH_TYPE (t) u##t +#define _ADDR_FETCH_TYPE (t) __ADDR_FETCH_TYPE(t) +#define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG) +#define __ASSIGN_FETCH_TYPE (_name, ptype, ftype, _size, sign, _fmttype) \ {.name = _name, \ .size = _size, \
@@ -186,13 +191,14 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(string); .fmt = PRINT_TYPE_FMT_NAME(ptype), \ .fmttype = _fmttype, \ } - +#define _ASSIGN_FETCH_TYPE (_name, ptype, ftype, _size, sign, _fmttype) \ + __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, #_fmttype)#define ASSIGN_FETCH_TYPE (ptype, ftype, sign) \ - __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) + _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype)/* If ptype is an alias of atype, use this macro (show atype in format) */ #define ASSIGN_FETCH_TYPE_ALIAS (ptype, atype, ftype, sign) \ - __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #atype) + _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)#define ASSIGN_FETCH_TYPE_END {}
Masami Hiramatsu
maintainerRFC PATCH 6/8 x86: ptrace: Add function argument access API
In reply to Masami HiramatsuAdd regs_get_argument() which returns N th argument of the function call.
Note that this chooses most probably assignment, in some case it can be incorrect (e.g. passing data structure or floating point etc.)
This is expected to be called from kprobes or ftrace with regs where the top of stack is the return address.
--- arch/Kconfig | 7 +++++++ arch/x86/Kconfig | 1 + arch/x86/include/asm/ptrace.h | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+)
arch/Kconfig +7 -0
@@ -258,6 +258,13 @@ config HAVE_REGS_AND_STACK_ACCESS_API declared in asm/ptrace.h For example the kprobes-based event tracer needs this API. +config HAVE_FUNCTION_ARG_ACCESS_API + bool + help + This symbol should be selected by an architecure if it supports + the API needed to access function arguments from pt_regs, + declared in asm/ptrace.h + config HAVE_CLK bool help
arch/x86/Kconfig +1 -0
@@ -172,6 +172,7 @@ config X86 select HAVE_PERF_USER_STACK_DUMP select HAVE_RCU_TABLE_FREE select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_RELIABLE_STACKTRACE if X86_64 && UNWINDER_FRAME_POINTER && STACK_VALIDATION select HAVE_STACK_VALIDATION if X86_64 select HAVE_SYSCALL_TRACEPOINTS
arch/x86/include/asm/ptrace.h +38 -0
@@ -251,6 +251,44 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,return 0 ; } +/** +* regs_get_kernel_argument() - get Nth function argument in kernel +* @regs: pt_regs of that context +* @n: function argument number (start from 0) +* +* regs_get_argument() returns @n th argument of the function call. +* Note that this chooses most probably assignment, in some case +* it can be incorrect. +* This is expected to be called from kprobes or ftrace with regs +* where the top of stack is the return address. +*/ +static inline unsigned long regs_get_kernel_argument (struct pt_regs *regs, +unsigned int n) +{ +const static unsigned int argument_offs[] = { +#ifdef __i386__ + offsetof(struct pt_regs , ax), + offsetof(struct pt_regs , cx), + offsetof(struct pt_regs , dx), +#define NR_REG_ARGUMENTS 3 +#else + offsetof(struct pt_regs , di), + offsetof(struct pt_regs , si), + offsetof(struct pt_regs , dx), + offsetof(struct pt_regs , cx), + offsetof(struct pt_regs , r8), + offsetof(struct pt_regs , r9), +#define NR_REG_ARGUMENTS 6 +#endif + }; + +if (n >=NR_REG_ARGUMENTS ) { + n -=NR_REG_ARGUMENTS -1 ; +return regs_get_kernel_stack_nth (regs, n); + }else +return regs_get_register (regs, argument_offs[n]); +} +#define arch_has_single_step () (1)#ifdef CONFIG_X86_DEBUGCTLMSR #define arch_has_block_step () (1)
Masami Hiramatsu
maintainerRFC PATCH 7/8 tracing: probeevent: Add $argN for accessing function args
In reply to Masami HiramatsuAdd $argN special fetch variable for accessing function arguments. This allows user to trace the Nth argument easily at the function entry.
Note that this returns most probably assignment of registers and stacks. In some case, it may not work well. If you need to access correct registers or stacks you should use perf-probe.
--- Documentation/trace/kprobetrace.txt | 10 ++++++---- kernel/trace/trace_kprobe.c | 18 +++++++++++++----- kernel/trace/trace_probe.c | 36 ++++++++++++++++++++++------------- kernel/trace/trace_probe.h | 9 ++++++++- kernel/trace/trace_uprobe.c | 2 +- 5 files changed, 51 insertions(+), 24 deletions(-)
Documentation/trace/kprobetrace.txt +6 -4
@@ -43,16 +43,18 @@ Synopsis of kprobe_events @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) $stackN : Fetch Nth entry of stack (N >= 0) $stack : Fetch stack address. - $retval : Fetch return value.(*) + $argN : Fetch the Nth function argument.(*1) + $retval : Fetch return value.(*2) $comm : Fetch current task comm. - +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) + +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(*3) NAME=FETCHARG : Set NAME as the argument name of FETCHARG. FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types (x8/x16/x32/x64), "string" and bitfield are supported. - (*) only for return probe. - (**) this is useful for fetching a field of data structures. + (*1) only for the probe on function entry (offset = 0) + (*2) only for return probe. + (*3) this is useful for fetching a field of data structures. Types -----
kernel/trace/trace_kprobe.c +13 -5
@@ -457,13 +457,15 @@ static int create_trace_kprobe(int argc, char **argv)unsigned long offset =0 ;void *addr =NULL ;char buf[MAX_EVENT_NAME_LEN ]; +unsigned int flags =TPARG_FL_KERNEL ;/* argc must be >= 1 */ if (argv[0 ][0 ] =='p' ) is_return = false; -else if (argv[0 ][0 ] =='r' ) +else if (argv[0 ][0 ] =='r' ) { is_return = true; -else if (argv[0 ][0 ] =='-' ) + flags |=TPARG_FL_RETURN ; + }else if (argv[0 ][0 ] =='-' ) is_delete = true;else { pr_info("Probe definition must be started with 'p', 'r' or"
@@ -546,8 +548,9 @@ static int create_trace_kprobe(int argc, char **argv)pr_info ("Failed to parse either an address or a symbol.\n" );return ret; } -if (offset && is_return && - !kprobe_on_func_entry (NULL , symbol, offset)) { +if (kprobe_on_func_entry (NULL , symbol, offset)) + flags |=TPARG_FL_FENTRY ; +if (offset && is_return && !(flags &TPARG_FL_FENTRY )) {pr_info ("Given offset is not valid for return probe.\n" );return -EINVAL ; }
@@ -617,7 +620,7 @@ static int create_trace_kprobe(int argc, char **argv)/* Parse fetch argument */ ret =traceprobe_parse_probe_arg (arg, &tk->tp.size, parg, - is_return, true); + flags);if (ret) {pr_info ("Parse error at argument[%d]. (%d)\n" , i, ret); goto error;
@@ -855,6 +858,11 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,case FETCH_OP_COMM : val = (unsigned long )current->comm;break ; +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API +case FETCH_OP_ARG : + val =regs_get_kernel_argument (regs, code->param); +break ; +#endif default :return -EILSEQ ; }
kernel/trace/trace_probe.c +23 -13
@@ -183,14 +183,13 @@ int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset)#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))static int parse_probe_vars (char *arg,const struct fetch_type *t, -struct fetch_insn *code,bool is_return, -bool is_kprobe) +struct fetch_insn *code,unsigned int flags) {int ret =0 ;unsigned long param;if (strcmp (arg,"retval" ) ==0 ) { -if (is_return) +if (flags &TPARG_FL_RETURN ) code->op =FETCH_OP_RETVAL ;else ret = -EINVAL ;
@@ -199,7 +198,8 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, code->op =FETCH_OP_STACKP ; }else if (isdigit (arg[5 ])) { ret =kstrtoul (arg +5 ,10 , ¶m); -if (ret || (is_kprobe && param >PARAM_MAX_STACK )) +if (ret || ((flags &TPARG_FL_KERNEL ) && + param >PARAM_MAX_STACK )) ret = -EINVAL ;else { code->op =FETCH_OP_STACK ;
@@ -209,6 +209,18 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, ret = -EINVAL ; }else if (strcmp (arg,"comm" ) ==0 ) { code->op =FETCH_OP_COMM ; +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API + }else if (((flags &TPARG_FL_MASK ) == + (TPARG_FL_KERNEL |TPARG_FL_FENTRY )) && +strncmp (arg,"arg" ,3 ) ==0 ) { +if (!isdigit (arg[3 ])) +return -EINVAL ; + ret =kstrtoul (arg +3 ,10 , ¶m); +if (ret || param >PARAM_MAX_STACK ) +return -EINVAL ; + code->op =FETCH_OP_ARG ; + code->param = (unsigned int )param; +#endif }else ret = -EINVAL ;
@@ -219,7 +231,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,static int parse_probe_arg (char *arg,const struct fetch_type *type,struct fetch_insn **pcode,struct fetch_insn *end, -bool is_return,bool is_kprobe) +unsigned int flags) {struct fetch_insn *code = *pcode;unsigned long param;
@@ -229,8 +241,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,switch (arg[0 ]) {case '$' : - ret =parse_probe_vars (arg +1 , type, code, - is_return, is_kprobe); + ret =parse_probe_vars (arg +1 , type, code, flags);break ;case '%' :/* named register */
@@ -252,7 +263,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->immediate = param; }else if (arg[1 ] =='+' ) {/* kprobes don't support file offsets */ -if (is_kprobe) +if (flags &TPARG_FL_KERNEL )return -EINVAL ; ret =kstrtol (arg +2 ,0 , &offset);
@@ -263,7 +274,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->immediate = (unsigned long )offset;// imm64? }else {/* uprobes don't support symbols */ -if (!is_kprobe) +if (!(flags &TPARG_FL_KERNEL ))return -EINVAL ; ret =traceprobe_split_symbol_offset (arg +1 , &offset);
@@ -304,8 +315,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,const struct fetch_type *t2 =find_fetch_type (NULL ); *tmp ='\0' ; - ret =parse_probe_arg (arg, t2, &code, end, is_return, - is_kprobe); + ret =parse_probe_arg (arg, t2, &code, end, flags);if (ret)break ;if (code->op ==FETCH_OP_COMM )
@@ -365,7 +375,7 @@ 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,unsigned int flags) {struct fetch_insn *code, *tmp =NULL ;const char *t;
@@ -405,7 +415,7 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, code[FETCH_INSN_MAX -1 ].op =FETCH_OP_END ; ret =parse_probe_arg (arg, parg->type, &code, &code[FETCH_INSN_MAX -1 ], - is_return, is_kprobe); + flags);if (ret) goto fail;
kernel/trace/trace_probe.h +8 -1
@@ -35,6 +35,7 @@#include <linux/stringify.h> #include <linux/limits.h> #include <linux/uaccess.h> +#include <linux/bitops.h> #include <asm/bitsperlong.h> #include "trace.h"
@@ -103,6 +104,7 @@ enum fetch_op {FETCH_OP_RETVAL ,/* Return value */ FETCH_OP_IMM ,/* Immediate : .immediate */ FETCH_OP_COMM ,/* Current comm */ +FETCH_OP_ARG ,/* Function argument : .param */ FETCH_OP_FOFFS ,/* File offset: .immediate */ // Stage 2 (dereference) op FETCH_OP_DEREF, /* Dereference: .offset */
@@ -260,8 +262,13 @@ find_event_file_link(struct trace_probe *tp, struct trace_event_file *file)return NULL ; } +#define TPARG_FL_RETURN BIT(0) +#define TPARG_FL_KERNEL BIT(1) +#define TPARG_FL_FENTRY BIT(2) +#define TPARG_FL_MASK GENMASK(2, 0) +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,unsigned int flags);extern int traceprobe_conflict_field_name (const char *name,struct probe_arg *args,int narg);
kernel/trace/trace_uprobe.c +1 -1
@@ -556,7 +556,7 @@ static int create_trace_uprobe(int argc, char **argv)/* Parse fetch argument */ ret =traceprobe_parse_probe_arg (arg, &tu->tp.size, parg, - is_return, false); + is_return ?TPARG_FL_RETURN :0 );if (ret) {pr_info ("Parse error at argument[%d]. (%d)\n" , i, ret); goto error;
Masami Hiramatsu
maintainerRFC PATCH 8/8 tracing: probeevent: Add an array for basic types
In reply to Masami HiramatsuAdd an array for basic types. This allows user to get arraied basic types from memory address.
The array type syntax is
TYPE[N]
Where TYPE is one of basic type (u8/16/32/64,s8/16/32/64, and x8/16/32/64) and N is a fixed value.
--- Documentation/trace/kprobetrace.txt | 7 ++ kernel/trace/trace_kprobe.c | 17 +++++ kernel/trace/trace_probe.c | 111 ++++++++++++++++++++++++++--------- kernel/trace/trace_probe.h | 29 ++++++++- kernel/trace/trace_uprobe.c | 17 +++++ 5 files changed, 144 insertions(+), 37 deletions(-)
Documentation/trace/kprobetrace.txt +7 -0
@@ -65,6 +65,13 @@ in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32' or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and x86-64 uses x64). +These value types can be an array. To record array data, you can add '[N]' +(where N is a fixed number, less than 64) to the base type. +E.g. 'x16[4]' means an array of x16 (2bytes hex) with 4 elements. +Note that the array can be applied to memory type fetchargs, you can not +apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is +wrong, but '+8($stack):x8[8]' is OK.) + String type is a special type, which fetches a "null-terminated" string from kernel space. This means it will fail and store NULL if the string container has been paged out.
kernel/trace/trace_kprobe.c +15 -2
@@ -835,8 +835,9 @@ static int process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, bool pre) { + struct fetch_insn *s3 = NULL;unsigned long val; -int ret; +int ret, i =0 ;/* 1st stage: get value from context */ switch (code->op) {
@@ -877,6 +878,8 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, code++; } +stage3: + s3 = code;/* 3rd stage: store value to buffer */ switch (code->op) {case FETCH_OP_ST_RAW :
@@ -902,6 +905,16 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, code++; } +/* the last stage: Loop on array */ +if (code->op ==FETCH_OP_LP_ARRAY ) { +if (++i < code->param) { + code = s3; + val += s3->size; + dest += s3->size; + goto stage3; + } + } +return code->op ==FETCH_OP_END ?0 : -EILSEQ ; }NOKPROBE_SYMBOL (process_fetch_insn)
@@ -1248,7 +1261,7 @@ static int register_kprobe_event(struct trace_kprobe *tk) call->event.funcs = &kprobe_funcs; call->class->define_fields = kprobe_event_define_fields; } -if (set_print_fmt (&tk->tp,trace_kprobe_is_return (tk)) <0 ) +if (traceprobe_set_print_fmt (&tk->tp,trace_kprobe_is_return (tk)) <0 )return -ENOMEM ; ret =register_trace_event (&call->event);if (!ret) {
kernel/trace/trace_probe.c +83 -28
181 lines not on this page · raw message
kernel/trace/trace_probe.h +24 -5
@@ -114,6 +114,8 @@ enum fetch_op {FETCH_OP_ST_STRING ,/* String: .offset, .size */ // Stage 4 (modify) op FETCH_OP_MOD_BF ,/* Bitfield: .basesize, .lshift, .rshift */ +// Stage 5 (loop) op +FETCH_OP_LP_ARRAY ,/* Array: .param = loop count */ FETCH_OP_END, };
@@ -203,13 +205,16 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol); _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)#define ASSIGN_FETCH_TYPE_END {} +#define MAX_ARRAY_LEN 64struct probe_arg {struct fetch_insn *code;bool dynamic;/* Dynamic array (string) is used */ unsigned int offset;/* Offset from argument entry */ +unsigned int count;/* Array count */ const char *name;/* Name of this argument */ const char *comm;/* Command of this argument */ +char *fmt;/* Format string if needed */ const struct fetch_type *type;/* Type of this argument */ };
@@ -385,16 +390,30 @@ static inline int print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, u8 *data, void *field) { - int i; + void *p; +int i, j;for (i =0 ; i < nr_args; i++) { -trace_seq_printf (s," %s=" , args[i].name); -if (!args[i].type->return -ENOMEM ; +struct probe_arg *a = args + i; + +trace_seq_printf (s," %s=" , a->name); +if (likely (!a->count)) { +if (!a->type->return -ENOMEM ; +continue ; + } +trace_seq_putc (s,'{' ); + p = data + a->offset; +for (j =0 ; j < a->count; j++) { +if (!a->type->return -ENOMEM ; +trace_seq_putc (s, j == a->count -1 ?'}' :',' ); + p += a->type->size; + } }return 0 ; } -extern int set_print_fmt (struct trace_probe *tp,bool is_return); +extern int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return);extern int traceprobe_define_arg_fields (struct trace_event_call *event_call,size_t offset,struct trace_probe *tp);
kernel/trace/trace_uprobe.c +15 -2
@@ -173,8 +173,9 @@ static int process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, bool pre) { + struct fetch_insn *s3 = NULL;unsigned long val; -int ret; +int ret, i =0 ;/* 1st stage: get value from context */ switch (code->op) {
@@ -210,6 +211,8 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, code++; } +stage3: + s3 = code;/* 3rd stage: store value to buffer */ switch (code->op) {case FETCH_OP_ST_RAW :
@@ -235,6 +238,16 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, code++; } +/* the last stage: Loop on array */ +if (code->op ==FETCH_OP_LP_ARRAY ) { +if (++i < code->param) { + code = s3; + val += s3->size; + dest += s3->size; + goto stage3; + } + } +return code->op ==FETCH_OP_END ?0 : -EILSEQ ; }NOKPROBE_SYMBOL (process_fetch_insn)
@@ -1303,7 +1316,7 @@ static int register_uprobe_event(struct trace_uprobe *tu) call->event.funcs = &uprobe_funcs; call->class->define_fields = uprobe_event_define_fields; -if (set_print_fmt (&tu->tp,is_ret_probe (tu)) <0 ) +if (traceprobe_set_print_fmt (&tu->tp,is_ret_probe (tu)) <0 )return -ENOMEM ; ret =register_trace_event (&call->event);
Steven Rostedt
maintainerQuoted 13 lines · On Wed, 14 Feb 2018 00:35:45 +0900 Masami Hiramatsu <[email protected]> wrote:
Hi,
This series improves probeevent variable fetcharg framework,
- Rewrite fetcharg framework with
fetch_insn, switch-case based instead of function pointer. (maybe good for retpoline)
- Add "symbol" type support, which shows symbol+offset instead of address value.
- Add "$argN" fetcharg, which fetches function parameters. (currently only for x86-64)
- Add array type support for basic types (except for string), which enables to get fixed length array from probeevents.
Quoted 1 line
All ideas came from Steve's function event tracer. Thanks Steve!
You're welcome ;-)
Quoted 4 lines
I'll add string (pointer) array support and testcases for these features in the next version.
Just to let you know I'm trying to catch up with some internal work and may not be able to look at these for a few days.
Hopefully others will be able to review them.
-- Steve