mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
tracing/probes: Add fprobe events for tracing function entry and exit.
Add fprobe events for tracing function entry and exit instead of kprobe events. With this change, we can continue to trace function entry/exit even if the CONFIG_KPROBES_ON_FTRACE is not available. Since CONFIG_KPROBES_ON_FTRACE requires the CONFIG_DYNAMIC_FTRACE_WITH_REGS, it is not available if the architecture only supports CONFIG_DYNAMIC_FTRACE_WITH_ARGS. And that means kprobe events can not probe function entry/exit effectively on such architecture. But this can be solved if the dynamic events supports fprobe events. The fprobe event is a new dynamic events which is only for the function (symbol) entry and exit. This event accepts non register fetch arguments so that user can trace the function arguments and return values. The fprobe events syntax is here; f[:[GRP/][EVENT]] FUNCTION [FETCHARGS] f[MAXACTIVE][:[GRP/][EVENT]] FUNCTION%return [FETCHARGS] E.g. # echo 'f vfs_read $arg1' >> dynamic_events # echo 'f vfs_read%return $retval' >> dynamic_events # cat dynamic_events f:fprobes/vfs_read__entry vfs_read arg1=$arg1 f:fprobes/vfs_read__exit vfs_read%return arg1=$retval # echo 1 > events/fprobes/enable # head -n 20 trace | tail # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | sh-142 [005] ...1. 448.386420: vfs_read__entry: (vfs_read+0x4/0x340) arg1=0xffff888007f7c540 sh-142 [005] ..... 448.386436: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=0x1 sh-142 [005] ...1. 448.386451: vfs_read__entry: (vfs_read+0x4/0x340) arg1=0xffff888007f7c540 sh-142 [005] ..... 448.386458: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=0x1 sh-142 [005] ...1. 448.386469: vfs_read__entry: (vfs_read+0x4/0x340) arg1=0xffff888007f7c540 sh-142 [005] ..... 448.386476: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=0x1 sh-142 [005] ...1. 448.602073: vfs_read__entry: (vfs_read+0x4/0x340) arg1=0xffff888007f7c540 sh-142 [005] ..... 448.602089: vfs_read__exit: (ksys_read+0x75/0x100 <- vfs_read) arg1=0x1 Link: https://lore.kernel.org/all/168507469754.913472.6112857614708350210.stgit@mhiramat.roam.corp.google.com/ Reported-by: kernel test robot <lkp@intel.com> Link: https://lore.kernel.org/all/202302011530.7vm4O8Ro-lkp@intel.com/ Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
This commit is contained in:
parent
30460c21ed
commit
334e5519c3
@ -66,6 +66,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
|
||||
int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num);
|
||||
int register_fprobe_syms(struct fprobe *fp, const char **syms, int num);
|
||||
int unregister_fprobe(struct fprobe *fp);
|
||||
bool fprobe_is_registered(struct fprobe *fp);
|
||||
#else
|
||||
static inline int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
|
||||
{
|
||||
@ -83,6 +84,10 @@ static inline int unregister_fprobe(struct fprobe *fp)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline bool fprobe_is_registered(struct fprobe *fp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -318,6 +318,7 @@ enum {
|
||||
TRACE_EVENT_FL_KPROBE_BIT,
|
||||
TRACE_EVENT_FL_UPROBE_BIT,
|
||||
TRACE_EVENT_FL_EPROBE_BIT,
|
||||
TRACE_EVENT_FL_FPROBE_BIT,
|
||||
TRACE_EVENT_FL_CUSTOM_BIT,
|
||||
};
|
||||
|
||||
@ -332,6 +333,7 @@ enum {
|
||||
* KPROBE - Event is a kprobe
|
||||
* UPROBE - Event is a uprobe
|
||||
* EPROBE - Event is an event probe
|
||||
* FPROBE - Event is an function probe
|
||||
* CUSTOM - Event is a custom event (to be attached to an exsiting tracepoint)
|
||||
* This is set when the custom event has not been attached
|
||||
* to a tracepoint yet, then it is cleared when it is.
|
||||
@ -346,6 +348,7 @@ enum {
|
||||
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
|
||||
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT),
|
||||
TRACE_EVENT_FL_EPROBE = (1 << TRACE_EVENT_FL_EPROBE_BIT),
|
||||
TRACE_EVENT_FL_FPROBE = (1 << TRACE_EVENT_FL_FPROBE_BIT),
|
||||
TRACE_EVENT_FL_CUSTOM = (1 << TRACE_EVENT_FL_CUSTOM_BIT),
|
||||
};
|
||||
|
||||
|
@ -650,6 +650,20 @@ config BLK_DEV_IO_TRACE
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config FPROBE_EVENTS
|
||||
depends on FPROBE
|
||||
depends on HAVE_REGS_AND_STACK_ACCESS_API
|
||||
bool "Enable fprobe-based dynamic events"
|
||||
select TRACING
|
||||
select PROBE_EVENTS
|
||||
select DYNAMIC_EVENTS
|
||||
default y
|
||||
help
|
||||
This allows user to add tracing events on the function entry and
|
||||
exit via ftrace interface. The syntax is same as the kprobe events
|
||||
and the kprobe events on function entry and exit will be
|
||||
transparently converted to this fprobe events.
|
||||
|
||||
config KPROBE_EVENTS
|
||||
depends on KPROBES
|
||||
depends on HAVE_REGS_AND_STACK_ACCESS_API
|
||||
|
@ -104,6 +104,7 @@ obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
|
||||
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o
|
||||
obj-$(CONFIG_FPROBE) += fprobe.o
|
||||
obj-$(CONFIG_RETHOOK) += rethook.o
|
||||
obj-$(CONFIG_FPROBE_EVENTS) += trace_fprobe.o
|
||||
|
||||
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
|
||||
obj-$(CONFIG_RV) += rv/
|
||||
|
@ -348,6 +348,14 @@ int register_fprobe_syms(struct fprobe *fp, const char **syms, int num)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_fprobe_syms);
|
||||
|
||||
bool fprobe_is_registered(struct fprobe *fp)
|
||||
{
|
||||
if (!fp || (fp->ops.saved_func != fprobe_handler &&
|
||||
fp->ops.saved_func != fprobe_kprobe_handler))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister_fprobe() - Unregister fprobe from ftrace
|
||||
* @fp: A fprobe data structure to be unregistered.
|
||||
@ -360,8 +368,7 @@ int unregister_fprobe(struct fprobe *fp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!fp || (fp->ops.saved_func != fprobe_handler &&
|
||||
fp->ops.saved_func != fprobe_kprobe_handler))
|
||||
if (!fprobe_is_registered(fp))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -5672,10 +5672,16 @@ static const char readme_msg[] =
|
||||
" uprobe_events\t\t- Create/append/remove/show the userspace dynamic events\n"
|
||||
"\t\t\t Write into this file to define/undefine new trace events.\n"
|
||||
#endif
|
||||
#if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS)
|
||||
#if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS) || \
|
||||
defined(CONFIG_FPROBE_EVENTS)
|
||||
"\t accepts: event-definitions (one definition per line)\n"
|
||||
#if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS)
|
||||
"\t Format: p[:[<group>/][<event>]] <place> [<args>]\n"
|
||||
"\t r[maxactive][:[<group>/][<event>]] <place> [<args>]\n"
|
||||
#endif
|
||||
#ifdef CONFIG_FPROBE_EVENTS
|
||||
"\t f[:[<group>/][<event>]] <func-name>[%return] [<args>]\n"
|
||||
#endif
|
||||
#ifdef CONFIG_HIST_TRIGGERS
|
||||
"\t s:[synthetic/]<event> <field> [<field>]\n"
|
||||
#endif
|
||||
|
@ -148,6 +148,17 @@ struct kretprobe_trace_entry_head {
|
||||
unsigned long ret_ip;
|
||||
};
|
||||
|
||||
struct fentry_trace_entry_head {
|
||||
struct trace_entry ent;
|
||||
unsigned long ip;
|
||||
};
|
||||
|
||||
struct fexit_trace_entry_head {
|
||||
struct trace_entry ent;
|
||||
unsigned long func;
|
||||
unsigned long ret_ip;
|
||||
};
|
||||
|
||||
#define TRACE_BUF_SIZE 1024
|
||||
|
||||
struct trace_array;
|
||||
|
1053
kernel/trace/trace_fprobe.c
Normal file
1053
kernel/trace/trace_fprobe.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -764,7 +764,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
|
||||
|
||||
if (isdigit(argv[0][1])) {
|
||||
if (!is_return) {
|
||||
trace_probe_log_err(1, MAXACT_NO_KPROBE);
|
||||
trace_probe_log_err(1, BAD_MAXACT_TYPE);
|
||||
goto parse_error;
|
||||
}
|
||||
if (event)
|
||||
|
@ -393,8 +393,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
|
||||
break;
|
||||
|
||||
case '%': /* named register */
|
||||
if (flags & TPARG_FL_TPOINT) {
|
||||
/* eprobes do not handle registers */
|
||||
if (flags & (TPARG_FL_TPOINT | TPARG_FL_FPROBE)) {
|
||||
/* eprobe and fprobe do not handle registers */
|
||||
trace_probe_log_err(offs, BAD_VAR);
|
||||
break;
|
||||
}
|
||||
|
@ -367,6 +367,7 @@ int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_a
|
||||
#define TPARG_FL_FENTRY BIT(2)
|
||||
#define TPARG_FL_TPOINT BIT(3)
|
||||
#define TPARG_FL_USER BIT(4)
|
||||
#define TPARG_FL_FPROBE BIT(5)
|
||||
#define TPARG_FL_MASK GENMASK(4, 0)
|
||||
|
||||
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
|
||||
@ -409,7 +410,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
||||
C(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
|
||||
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
|
||||
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \
|
||||
C(MAXACT_NO_KPROBE, "Maxactive is not for kprobe"), \
|
||||
C(BAD_MAXACT_TYPE, "Maxactive is only for function exit"), \
|
||||
C(BAD_MAXACT, "Invalid maxactive number"), \
|
||||
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
|
||||
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
|
||||
|
@ -8,7 +8,7 @@ check_error() { # command-with-error-pos-by-^
|
||||
}
|
||||
|
||||
if grep -q 'r\[maxactive\]' README; then
|
||||
check_error 'p^100 vfs_read' # MAXACT_NO_KPROBE
|
||||
check_error 'p^100 vfs_read' # BAD_MAXACT_TYPE
|
||||
check_error 'r^1a111 vfs_read' # BAD_MAXACT
|
||||
check_error 'r^100000 vfs_read' # MAXACT_TOO_BIG
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user