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: default avatarkernel test robot <lkp@intel.com>
Link: https://lore.kernel.org/all/202302011530.7vm4O8Ro-lkp@intel.com/Signed-off-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
parent 30460c21
...@@ -66,6 +66,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter ...@@ -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_ips(struct fprobe *fp, unsigned long *addrs, int num);
int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); int register_fprobe_syms(struct fprobe *fp, const char **syms, int num);
int unregister_fprobe(struct fprobe *fp); int unregister_fprobe(struct fprobe *fp);
bool fprobe_is_registered(struct fprobe *fp);
#else #else
static inline int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter) 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) ...@@ -83,6 +84,10 @@ static inline int unregister_fprobe(struct fprobe *fp)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline bool fprobe_is_registered(struct fprobe *fp)
{
return false;
}
#endif #endif
/** /**
......
...@@ -318,6 +318,7 @@ enum { ...@@ -318,6 +318,7 @@ enum {
TRACE_EVENT_FL_KPROBE_BIT, TRACE_EVENT_FL_KPROBE_BIT,
TRACE_EVENT_FL_UPROBE_BIT, TRACE_EVENT_FL_UPROBE_BIT,
TRACE_EVENT_FL_EPROBE_BIT, TRACE_EVENT_FL_EPROBE_BIT,
TRACE_EVENT_FL_FPROBE_BIT,
TRACE_EVENT_FL_CUSTOM_BIT, TRACE_EVENT_FL_CUSTOM_BIT,
}; };
...@@ -332,6 +333,7 @@ enum { ...@@ -332,6 +333,7 @@ enum {
* KPROBE - Event is a kprobe * KPROBE - Event is a kprobe
* UPROBE - Event is a uprobe * UPROBE - Event is a uprobe
* EPROBE - Event is an event probe * 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) * CUSTOM - Event is a custom event (to be attached to an exsiting tracepoint)
* This is set when the custom event has not been attached * This is set when the custom event has not been attached
* to a tracepoint yet, then it is cleared when it is. * to a tracepoint yet, then it is cleared when it is.
...@@ -346,6 +348,7 @@ enum { ...@@ -346,6 +348,7 @@ enum {
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT), TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT), TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT),
TRACE_EVENT_FL_EPROBE = (1 << TRACE_EVENT_FL_EPROBE_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), TRACE_EVENT_FL_CUSTOM = (1 << TRACE_EVENT_FL_CUSTOM_BIT),
}; };
......
...@@ -650,6 +650,20 @@ config BLK_DEV_IO_TRACE ...@@ -650,6 +650,20 @@ config BLK_DEV_IO_TRACE
If unsure, say N. 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 config KPROBE_EVENTS
depends on KPROBES depends on KPROBES
depends on HAVE_REGS_AND_STACK_ACCESS_API depends on HAVE_REGS_AND_STACK_ACCESS_API
......
...@@ -104,6 +104,7 @@ obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o ...@@ -104,6 +104,7 @@ obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o
obj-$(CONFIG_FPROBE) += fprobe.o obj-$(CONFIG_FPROBE) += fprobe.o
obj-$(CONFIG_RETHOOK) += rethook.o obj-$(CONFIG_RETHOOK) += rethook.o
obj-$(CONFIG_FPROBE_EVENTS) += trace_fprobe.o
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
obj-$(CONFIG_RV) += rv/ obj-$(CONFIG_RV) += rv/
......
...@@ -348,6 +348,14 @@ int register_fprobe_syms(struct fprobe *fp, const char **syms, int num) ...@@ -348,6 +348,14 @@ int register_fprobe_syms(struct fprobe *fp, const char **syms, int num)
} }
EXPORT_SYMBOL_GPL(register_fprobe_syms); 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 * unregister_fprobe() - Unregister fprobe from ftrace
* @fp: A fprobe data structure to be unregistered. * @fp: A fprobe data structure to be unregistered.
...@@ -360,8 +368,7 @@ int unregister_fprobe(struct fprobe *fp) ...@@ -360,8 +368,7 @@ int unregister_fprobe(struct fprobe *fp)
{ {
int ret; int ret;
if (!fp || (fp->ops.saved_func != fprobe_handler && if (!fprobe_is_registered(fp))
fp->ops.saved_func != fprobe_kprobe_handler))
return -EINVAL; return -EINVAL;
/* /*
......
...@@ -5672,10 +5672,16 @@ static const char readme_msg[] = ...@@ -5672,10 +5672,16 @@ static const char readme_msg[] =
" uprobe_events\t\t- Create/append/remove/show the userspace dynamic events\n" " 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" "\t\t\t Write into this file to define/undefine new trace events.\n"
#endif #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" "\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 Format: p[:[<group>/][<event>]] <place> [<args>]\n"
"\t r[maxactive][:[<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 #ifdef CONFIG_HIST_TRIGGERS
"\t s:[synthetic/]<event> <field> [<field>]\n" "\t s:[synthetic/]<event> <field> [<field>]\n"
#endif #endif
......
...@@ -148,6 +148,17 @@ struct kretprobe_trace_entry_head { ...@@ -148,6 +148,17 @@ struct kretprobe_trace_entry_head {
unsigned long ret_ip; 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 #define TRACE_BUF_SIZE 1024
struct trace_array; struct trace_array;
......
This diff is collapsed.
...@@ -764,7 +764,7 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -764,7 +764,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
if (isdigit(argv[0][1])) { if (isdigit(argv[0][1])) {
if (!is_return) { if (!is_return) {
trace_probe_log_err(1, MAXACT_NO_KPROBE); trace_probe_log_err(1, BAD_MAXACT_TYPE);
goto parse_error; goto parse_error;
} }
if (event) if (event)
......
...@@ -393,8 +393,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -393,8 +393,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
break; break;
case '%': /* named register */ case '%': /* named register */
if (flags & TPARG_FL_TPOINT) { if (flags & (TPARG_FL_TPOINT | TPARG_FL_FPROBE)) {
/* eprobes do not handle registers */ /* eprobe and fprobe do not handle registers */
trace_probe_log_err(offs, BAD_VAR); trace_probe_log_err(offs, BAD_VAR);
break; break;
} }
......
...@@ -367,6 +367,7 @@ int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_a ...@@ -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_FENTRY BIT(2)
#define TPARG_FL_TPOINT BIT(3) #define TPARG_FL_TPOINT BIT(3)
#define TPARG_FL_USER BIT(4) #define TPARG_FL_USER BIT(4)
#define TPARG_FL_FPROBE BIT(5)
#define TPARG_FL_MASK GENMASK(4, 0) #define TPARG_FL_MASK GENMASK(4, 0)
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, 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, ...@@ -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(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \ C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \ 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(BAD_MAXACT, "Invalid maxactive number"), \
C(MAXACT_TOO_BIG, "Maxactive is too big"), \ C(MAXACT_TOO_BIG, "Maxactive is too big"), \
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \ C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
......
...@@ -8,7 +8,7 @@ check_error() { # command-with-error-pos-by-^ ...@@ -8,7 +8,7 @@ check_error() { # command-with-error-pos-by-^
} }
if grep -q 'r\[maxactive\]' README; then 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^1a111 vfs_read' # BAD_MAXACT
check_error 'r^100000 vfs_read' # MAXACT_TOO_BIG check_error 'r^100000 vfs_read' # MAXACT_TOO_BIG
fi fi
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment