Commit d2a6fd45 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'probes-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull probes updates from Masami Hiramatsu:

 - fprobe: Pass return address to the fprobe entry/exit callbacks so
   that the callbacks don't need to analyze pt_regs/stack to find the
   function return address.

 - kprobe events: cleanup usage of TPARG_FL_FENTRY and TPARG_FL_RETURN
   flags so that those are not set at once.

 - fprobe events:
      - Add a new fprobe events for tracing arbitrary function entry and
        exit as a trace event.
      - Add a new tracepoint events for tracing raw tracepoint as a
        trace event. This allows user to trace non user-exposed
        tracepoints.
      - Move eprobe's event parser code into probe event common file.
      - Introduce BTF (BPF type format) support to kernel probe (kprobe,
        fprobe and tracepoint probe) events so that user can specify
        traced function arguments by name. This also applies the type of
        argument when fetching the argument.
      - Introduce '$arg*' wildcard support if BTF is available. This
        expands the '$arg*' meta argument to all function argument
        automatically.
      - Check the return value types by BTF. If the function returns
        'void', '$retval' is rejected.
      - Add some selftest script for fprobe events, tracepoint events
        and BTF support.
      - Update documentation about the fprobe events.
      - Some fixes for above features, document and selftests.

 - selftests for ftrace (in addition to the new fprobe events):
      - Add a test case for multiple consecutive probes in a function
        which checks if ftrace based kprobe, optimized kprobe and normal
        kprobe can be defined in the same target function.
      - Add a test case for optimized probe, which checks whether kprobe
        can be optimized or not.

* tag 'probes-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tracing/probes: Fix tracepoint event with $arg* to fetch correct argument
  Documentation: Fix typo of reference file name
  tracing/probes: Fix to return NULL and keep using current argc
  selftests/ftrace: Add new test case which checks for optimized probes
  selftests/ftrace: Add new test case which adds multiple consecutive probes in a function
  Documentation: tracing/probes: Add fprobe event tracing document
  selftests/ftrace: Add BTF arguments test cases
  selftests/ftrace: Add tracepoint probe test case
  tracing/probes: Add BTF retval type support
  tracing/probes: Add $arg* meta argument for all function args
  tracing/probes: Support function parameters if BTF is available
  tracing/probes: Move event parameter fetching code to common parser
  tracing/probes: Add tracepoint support on fprobe_events
  selftests/ftrace: Add fprobe related testcases
  tracing/probes: Add fprobe events for tracing function entry and exit.
  tracing/probes: Avoid setting TPARG_FL_FENTRY and TPARG_FL_RETURN
  fprobe: Pass return address to the handlers
parents cccf0c2e 53431798
This diff is collapsed.
...@@ -13,6 +13,7 @@ Linux Tracing Technologies ...@@ -13,6 +13,7 @@ Linux Tracing Technologies
kprobes kprobes
kprobetrace kprobetrace
uprobetracer uprobetracer
fprobetrace
tracepoints tracepoints
events events
events-kmem events-kmem
......
...@@ -66,6 +66,8 @@ Synopsis of kprobe_events ...@@ -66,6 +66,8 @@ Synopsis of kprobe_events
(\*3) this is useful for fetching a field of data structures. (\*3) this is useful for fetching a field of data structures.
(\*4) "u" means user-space dereference. See :ref:`user_mem_access`. (\*4) "u" means user-space dereference. See :ref:`user_mem_access`.
.. _kprobetrace_types:
Types Types
----- -----
Several types are supported for fetchargs. Kprobe tracer will access memory Several types are supported for fetchargs. Kprobe tracer will access memory
......
...@@ -35,9 +35,11 @@ struct fprobe { ...@@ -35,9 +35,11 @@ struct fprobe {
int nr_maxactive; int nr_maxactive;
int (*entry_handler)(struct fprobe *fp, unsigned long entry_ip, int (*entry_handler)(struct fprobe *fp, unsigned long entry_ip,
struct pt_regs *regs, void *entry_data); unsigned long ret_ip, struct pt_regs *regs,
void *entry_data);
void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip, void (*exit_handler)(struct fprobe *fp, unsigned long entry_ip,
struct pt_regs *regs, void *entry_data); unsigned long ret_ip, struct pt_regs *regs,
void *entry_data);
}; };
/* This fprobe is soft-disabled. */ /* This fprobe is soft-disabled. */
...@@ -64,6 +66,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter ...@@ -64,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)
{ {
...@@ -81,6 +84,10 @@ static inline int unregister_fprobe(struct fprobe *fp) ...@@ -81,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
/** /**
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
struct rethook_node; struct rethook_node;
typedef void (*rethook_handler_t) (struct rethook_node *, void *, struct pt_regs *); typedef void (*rethook_handler_t) (struct rethook_node *, void *, unsigned long, struct pt_regs *);
/** /**
* struct rethook - The rethook management data structure. * struct rethook - The rethook management data structure.
......
...@@ -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),
}; };
......
...@@ -35,6 +35,7 @@ struct tracepoint { ...@@ -35,6 +35,7 @@ struct tracepoint {
struct static_call_key *static_call_key; struct static_call_key *static_call_key;
void *static_call_tramp; void *static_call_tramp;
void *iterator; void *iterator;
void *probestub;
int (*regfunc)(void); int (*regfunc)(void);
void (*unregfunc)(void); void (*unregfunc)(void);
struct tracepoint_func __rcu *funcs; struct tracepoint_func __rcu *funcs;
......
...@@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) ...@@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
__section("__tracepoints_strings") = #_name; \ __section("__tracepoints_strings") = #_name; \
extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \
int __traceiter_##_name(void *__data, proto); \ int __traceiter_##_name(void *__data, proto); \
void __probestub_##_name(void *__data, proto); \
struct tracepoint __tracepoint_##_name __used \ struct tracepoint __tracepoint_##_name __used \
__section("__tracepoints") = { \ __section("__tracepoints") = { \
.name = __tpstrtab_##_name, \ .name = __tpstrtab_##_name, \
...@@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) ...@@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
.static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \
.static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \
.iterator = &__traceiter_##_name, \ .iterator = &__traceiter_##_name, \
.probestub = &__probestub_##_name, \
.regfunc = _reg, \ .regfunc = _reg, \
.unregfunc = _unreg, \ .unregfunc = _unreg, \
.funcs = NULL }; \ .funcs = NULL }; \
...@@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) ...@@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
} \ } \
return 0; \ return 0; \
} \ } \
void __probestub_##_name(void *__data, proto) \
{ \
} \
DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name);
#define DEFINE_TRACE(name, proto, args) \ #define DEFINE_TRACE(name, proto, args) \
......
...@@ -2127,6 +2127,7 @@ static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs) ...@@ -2127,6 +2127,7 @@ static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
NOKPROBE_SYMBOL(pre_handler_kretprobe); NOKPROBE_SYMBOL(pre_handler_kretprobe);
static void kretprobe_rethook_handler(struct rethook_node *rh, void *data, static void kretprobe_rethook_handler(struct rethook_node *rh, void *data,
unsigned long ret_addr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct kretprobe *rp = (struct kretprobe *)data; struct kretprobe *rp = (struct kretprobe *)data;
......
...@@ -665,6 +665,32 @@ config BLK_DEV_IO_TRACE ...@@ -665,6 +665,32 @@ 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 PROBE_EVENTS_BTF_ARGS
depends on HAVE_FUNCTION_ARG_ACCESS_API
depends on FPROBE_EVENTS || KPROBE_EVENTS
depends on DEBUG_INFO_BTF && BPF_SYSCALL
bool "Support BTF function arguments for probe events"
default y
help
The user can specify the arguments of the probe event using the names
of the arguments of the probed function, when the probe location is a
kernel function entry or a tracepoint.
This is available only if BTF (BPF Type Format) support is enabled.
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/
......
...@@ -2652,7 +2652,8 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link, ...@@ -2652,7 +2652,8 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
static int static int
kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip, kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
struct pt_regs *regs, void *data) unsigned long ret_ip, struct pt_regs *regs,
void *data)
{ {
struct bpf_kprobe_multi_link *link; struct bpf_kprobe_multi_link *link;
...@@ -2663,7 +2664,8 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip, ...@@ -2663,7 +2664,8 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,
static void static void
kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip, kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip,
struct pt_regs *regs, void *data) unsigned long ret_ip, struct pt_regs *regs,
void *data)
{ {
struct bpf_kprobe_multi_link *link; struct bpf_kprobe_multi_link *link;
......
...@@ -46,7 +46,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip, ...@@ -46,7 +46,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip,
} }
if (fp->entry_handler) if (fp->entry_handler)
ret = fp->entry_handler(fp, ip, ftrace_get_regs(fregs), entry_data); ret = fp->entry_handler(fp, ip, parent_ip, ftrace_get_regs(fregs), entry_data);
/* If entry_handler returns !0, nmissed is not counted. */ /* If entry_handler returns !0, nmissed is not counted. */
if (rh) { if (rh) {
...@@ -112,7 +112,7 @@ static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, ...@@ -112,7 +112,7 @@ static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
} }
static void fprobe_exit_handler(struct rethook_node *rh, void *data, static void fprobe_exit_handler(struct rethook_node *rh, void *data,
struct pt_regs *regs) unsigned long ret_ip, struct pt_regs *regs)
{ {
struct fprobe *fp = (struct fprobe *)data; struct fprobe *fp = (struct fprobe *)data;
struct fprobe_rethook_node *fpr; struct fprobe_rethook_node *fpr;
...@@ -133,7 +133,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data, ...@@ -133,7 +133,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
return; return;
} }
fp->exit_handler(fp, fpr->entry_ip, regs, fp->exit_handler(fp, fpr->entry_ip, ret_ip, regs,
fp->entry_data_size ? (void *)fpr->data : NULL); fp->entry_data_size ? (void *)fpr->data : NULL);
ftrace_test_recursion_unlock(bit); ftrace_test_recursion_unlock(bit);
} }
...@@ -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;
/* /*
......
...@@ -301,7 +301,8 @@ unsigned long rethook_trampoline_handler(struct pt_regs *regs, ...@@ -301,7 +301,8 @@ unsigned long rethook_trampoline_handler(struct pt_regs *regs,
break; break;
handler = READ_ONCE(rhn->rethook->handler); handler = READ_ONCE(rhn->rethook->handler);
if (handler) if (handler)
handler(rhn, rhn->rethook->data, regs); handler(rhn, rhn->rethook->data,
correct_ret_addr, regs);
if (first == node) if (first == node)
break; break;
......
...@@ -5672,10 +5672,17 @@ static const char readme_msg[] = ...@@ -5672,10 +5672,17 @@ 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"
"\t t[:[<group>/][<event>]] <tracepoint> [<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
...@@ -5691,7 +5698,11 @@ static const char readme_msg[] = ...@@ -5691,7 +5698,11 @@ static const char readme_msg[] =
"\t args: <name>=fetcharg[:type]\n" "\t args: <name>=fetcharg[:type]\n"
"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n" "\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
"\t $stack<index>, $stack, $retval, $comm, $arg<N>, <argname>\n"
#else
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n" "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
#endif
#else #else
"\t $stack<index>, $stack, $retval, $comm,\n" "\t $stack<index>, $stack, $retval, $comm,\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;
......
...@@ -227,37 +227,6 @@ static struct trace_eprobe *alloc_event_probe(const char *group, ...@@ -227,37 +227,6 @@ static struct trace_eprobe *alloc_event_probe(const char *group,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
static int trace_eprobe_tp_arg_update(struct trace_eprobe *ep, int i)
{
struct probe_arg *parg = &ep->tp.args[i];
struct ftrace_event_field *field;
struct list_head *head;
int ret = -ENOENT;
head = trace_get_fields(ep->event);
list_for_each_entry(field, head, link) {
if (!strcmp(parg->code->data, field->name)) {
kfree(parg->code->data);
parg->code->data = field;
return 0;
}
}
/*
* Argument not found on event. But allow for comm and COMM
* to be used to get the current->comm.
*/
if (strcmp(parg->code->data, "COMM") == 0 ||
strcmp(parg->code->data, "comm") == 0) {
parg->code->op = FETCH_OP_COMM;
ret = 0;
}
kfree(parg->code->data);
parg->code->data = NULL;
return ret;
}
static int eprobe_event_define_fields(struct trace_event_call *event_call) static int eprobe_event_define_fields(struct trace_event_call *event_call)
{ {
struct eprobe_trace_entry_head field; struct eprobe_trace_entry_head field;
...@@ -817,19 +786,16 @@ find_and_get_event(const char *system, const char *event_name) ...@@ -817,19 +786,16 @@ find_and_get_event(const char *system, const char *event_name)
static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[], int i) static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[], int i)
{ {
unsigned int flags = TPARG_FL_KERNEL | TPARG_FL_TPOINT; struct traceprobe_parse_context ctx = {
.event = ep->event,
.flags = TPARG_FL_KERNEL | TPARG_FL_TEVENT,
};
int ret; int ret;
ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], flags); ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], &ctx);
if (ret) if (ret)
return ret; return ret;
if (ep->tp.args[i].code->op == FETCH_OP_TP_ARG) {
ret = trace_eprobe_tp_arg_update(ep, i);
if (ret)
trace_probe_log_err(0, BAD_ATTACH_ARG);
}
/* Handle symbols "@" */ /* Handle symbols "@" */
if (!ret) if (!ret)
ret = traceprobe_update_arg(&ep->tp.args[i]); ret = traceprobe_update_arg(&ep->tp.args[i]);
......
This diff is collapsed.
...@@ -732,9 +732,10 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -732,9 +732,10 @@ static int __trace_kprobe_create(int argc, const char *argv[])
* FETCHARG:TYPE : use TYPE instead of unsigned long. * FETCHARG:TYPE : use TYPE instead of unsigned long.
*/ */
struct trace_kprobe *tk = NULL; struct trace_kprobe *tk = NULL;
int i, len, ret = 0; int i, len, new_argc = 0, ret = 0;
bool is_return = false; bool is_return = false;
char *symbol = NULL, *tmp = NULL; char *symbol = NULL, *tmp = NULL;
const char **new_argv = NULL;
const char *event = NULL, *group = KPROBE_EVENT_SYSTEM; const char *event = NULL, *group = KPROBE_EVENT_SYSTEM;
enum probe_print_type ptype; enum probe_print_type ptype;
int maxactive = 0; int maxactive = 0;
...@@ -742,7 +743,8 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -742,7 +743,8 @@ static int __trace_kprobe_create(int argc, const char *argv[])
void *addr = NULL; void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN]; char buf[MAX_EVENT_NAME_LEN];
char gbuf[MAX_EVENT_NAME_LEN]; char gbuf[MAX_EVENT_NAME_LEN];
unsigned int flags = TPARG_FL_KERNEL; char abuf[MAX_BTF_ARGS_LEN];
struct traceprobe_parse_context ctx = { .flags = TPARG_FL_KERNEL };
switch (argv[0][0]) { switch (argv[0][0]) {
case 'r': case 'r':
...@@ -764,7 +766,7 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -764,7 +766,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)
...@@ -823,10 +825,10 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -823,10 +825,10 @@ static int __trace_kprobe_create(int argc, const char *argv[])
goto parse_error; goto parse_error;
} }
if (is_return) if (is_return)
flags |= TPARG_FL_RETURN; ctx.flags |= TPARG_FL_RETURN;
ret = kprobe_on_func_entry(NULL, symbol, offset); ret = kprobe_on_func_entry(NULL, symbol, offset);
if (ret == 0) if (ret == 0 && !is_return)
flags |= TPARG_FL_FENTRY; ctx.flags |= TPARG_FL_FENTRY;
/* Defer the ENOENT case until register kprobe */ /* Defer the ENOENT case until register kprobe */
if (ret == -EINVAL && is_return) { if (ret == -EINVAL && is_return) {
trace_probe_log_err(0, BAD_RETPROBE); trace_probe_log_err(0, BAD_RETPROBE);
...@@ -854,21 +856,35 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -854,21 +856,35 @@ static int __trace_kprobe_create(int argc, const char *argv[])
event = buf; event = buf;
} }
argc -= 2; argv += 2;
ctx.funcname = symbol;
new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,
abuf, MAX_BTF_ARGS_LEN, &ctx);
if (IS_ERR(new_argv)) {
ret = PTR_ERR(new_argv);
new_argv = NULL;
goto out;
}
if (new_argv) {
argc = new_argc;
argv = new_argv;
}
/* setup a probe */ /* setup a probe */
tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive, tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive,
argc - 2, is_return); argc, is_return);
if (IS_ERR(tk)) { if (IS_ERR(tk)) {
ret = PTR_ERR(tk); ret = PTR_ERR(tk);
/* This must return -ENOMEM, else there is a bug */ /* This must return -ENOMEM, else there is a bug */
WARN_ON_ONCE(ret != -ENOMEM); WARN_ON_ONCE(ret != -ENOMEM);
goto out; /* We know tk is not allocated */ goto out; /* We know tk is not allocated */
} }
argc -= 2; argv += 2;
/* parse arguments */ /* parse arguments */
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
trace_probe_log_set_index(i + 2); trace_probe_log_set_index(i + 2);
ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], flags); ctx.offset = 0;
ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], &ctx);
if (ret) if (ret)
goto error; /* This can be -ENOMEM */ goto error; /* This can be -ENOMEM */
} }
...@@ -892,6 +908,7 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -892,6 +908,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
out: out:
trace_probe_log_clear(); trace_probe_log_clear();
kfree(new_argv);
kfree(symbol); kfree(symbol);
return ret; return ret;
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/btf.h>
#include <asm/bitsperlong.h> #include <asm/bitsperlong.h>
#include "trace.h" #include "trace.h"
...@@ -32,7 +33,9 @@ ...@@ -32,7 +33,9 @@
#define MAX_ARGSTR_LEN 63 #define MAX_ARGSTR_LEN 63
#define MAX_ARRAY_LEN 64 #define MAX_ARRAY_LEN 64
#define MAX_ARG_NAME_LEN 32 #define MAX_ARG_NAME_LEN 32
#define MAX_BTF_ARGS_LEN 128
#define MAX_STRING_SIZE PATH_MAX #define MAX_STRING_SIZE PATH_MAX
#define MAX_ARG_BUF_LEN (MAX_TRACE_ARGS * MAX_ARG_NAME_LEN)
/* Reserved field names */ /* Reserved field names */
#define FIELD_STRING_IP "__probe_ip" #define FIELD_STRING_IP "__probe_ip"
...@@ -357,15 +360,42 @@ int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_a ...@@ -357,15 +360,42 @@ int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_a
#define trace_probe_for_each_link_rcu(pos, tp) \ #define trace_probe_for_each_link_rcu(pos, tp) \
list_for_each_entry_rcu(pos, &(tp)->event->files, list) list_for_each_entry_rcu(pos, &(tp)->event->files, list)
/*
* The flags used for parsing trace_probe arguments.
* TPARG_FL_RETURN, TPARG_FL_FENTRY and TPARG_FL_TEVENT are mutually exclusive.
* TPARG_FL_KERNEL and TPARG_FL_USER are also mutually exclusive.
* TPARG_FL_FPROBE and TPARG_FL_TPOINT are optional but it should be with
* TPARG_FL_KERNEL.
*/
#define TPARG_FL_RETURN BIT(0) #define TPARG_FL_RETURN BIT(0)
#define TPARG_FL_KERNEL BIT(1) #define TPARG_FL_KERNEL BIT(1)
#define TPARG_FL_FENTRY BIT(2) #define TPARG_FL_FENTRY BIT(2)
#define TPARG_FL_TPOINT BIT(3) #define TPARG_FL_TEVENT BIT(3)
#define TPARG_FL_USER BIT(4) #define TPARG_FL_USER BIT(4)
#define TPARG_FL_MASK GENMASK(4, 0) #define TPARG_FL_FPROBE BIT(5)
#define TPARG_FL_TPOINT BIT(6)
#define TPARG_FL_LOC_MASK GENMASK(4, 0)
static inline bool tparg_is_function_entry(unsigned int flags)
{
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_FENTRY);
}
struct traceprobe_parse_context {
struct trace_event_call *event;
const struct btf_param *params;
s32 nr_params;
const char *funcname;
unsigned int flags;
int offset;
};
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
const char *argv, unsigned int flags); const char *argv,
struct traceprobe_parse_context *ctx);
const char **traceprobe_expand_meta_args(int argc, const char *argv[],
int *new_argc, char *buf, int bufsize,
struct traceprobe_parse_context *ctx);
extern int traceprobe_update_arg(struct probe_arg *arg); extern int traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg); extern void traceprobe_free_probe_arg(struct probe_arg *arg);
...@@ -404,11 +434,12 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -404,11 +434,12 @@ 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"), \
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \ C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
C(NO_TRACEPOINT, "Tracepoint is not found"), \
C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \ C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \
C(NO_GROUP_NAME, "Group name is not specified"), \ C(NO_GROUP_NAME, "Group name is not specified"), \
C(GROUP_TOO_LONG, "Group name is too long"), \ C(GROUP_TOO_LONG, "Group name is too long"), \
...@@ -418,6 +449,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -418,6 +449,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \ C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \
C(EVENT_EXIST, "Given group/event name is already used by another event"), \ C(EVENT_EXIST, "Given group/event name is already used by another event"), \
C(RETVAL_ON_PROBE, "$retval is not available on probe"), \ C(RETVAL_ON_PROBE, "$retval is not available on probe"), \
C(NO_RETVAL, "This function returns 'void' type"), \
C(BAD_STACK_NUM, "Invalid stack number"), \ C(BAD_STACK_NUM, "Invalid stack number"), \
C(BAD_ARG_NUM, "Invalid argument number"), \ C(BAD_ARG_NUM, "Invalid argument number"), \
C(BAD_VAR, "Invalid $-valiable specified"), \ C(BAD_VAR, "Invalid $-valiable specified"), \
...@@ -456,7 +488,14 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -456,7 +488,14 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(NO_EVENT_INFO, "This requires both group and event name to attach"),\ C(NO_EVENT_INFO, "This requires both group and event name to attach"),\
C(BAD_ATTACH_EVENT, "Attached event does not exist"),\ C(BAD_ATTACH_EVENT, "Attached event does not exist"),\
C(BAD_ATTACH_ARG, "Attached event does not have this field"),\ C(BAD_ATTACH_ARG, "Attached event does not have this field"),\
C(NO_EP_FILTER, "No filter rule after 'if'"), C(NO_EP_FILTER, "No filter rule after 'if'"), \
C(NOSUP_BTFARG, "BTF is not available or not supported"), \
C(NO_BTFARG, "This variable is not found at this probe point"),\
C(NO_BTF_ENTRY, "No BTF entry for this probe point"), \
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\
C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
C(ARGS_2LONG, "$arg* failed because the argument list is too long"),
#undef C #undef C
#define C(a, b) TP_ERR_##a #define C(a, b) TP_ERR_##a
......
...@@ -686,10 +686,12 @@ static int __trace_uprobe_create(int argc, const char **argv) ...@@ -686,10 +686,12 @@ static int __trace_uprobe_create(int argc, const char **argv)
/* parse arguments */ /* parse arguments */
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
struct traceprobe_parse_context ctx = {
.flags = (is_return ? TPARG_FL_RETURN : 0) | TPARG_FL_USER,
};
trace_probe_log_set_index(i + 2); trace_probe_log_set_index(i + 2);
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], &ctx);
(is_return ? TPARG_FL_RETURN : 0) |
TPARG_FL_USER);
if (ret) if (ret)
goto error; goto error;
} }
......
...@@ -39,7 +39,8 @@ static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32)) ...@@ -39,7 +39,8 @@ static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
} }
static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
struct pt_regs *regs, void *data) unsigned long ret_ip,
struct pt_regs *regs, void *data)
{ {
KUNIT_EXPECT_FALSE(current_test, preemptible()); KUNIT_EXPECT_FALSE(current_test, preemptible());
/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */ /* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
...@@ -57,6 +58,7 @@ static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, ...@@ -57,6 +58,7 @@ static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
} }
static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
unsigned long ret_ip,
struct pt_regs *regs, void *data) struct pt_regs *regs, void *data)
{ {
unsigned long ret = regs_return_value(regs); unsigned long ret = regs_return_value(regs);
...@@ -78,14 +80,16 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, ...@@ -78,14 +80,16 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
} }
static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
struct pt_regs *regs, void *data) unsigned long ret_ip,
struct pt_regs *regs, void *data)
{ {
KUNIT_EXPECT_FALSE(current_test, preemptible()); KUNIT_EXPECT_FALSE(current_test, preemptible());
return 0; return 0;
} }
static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip, static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
struct pt_regs *regs, void *data) unsigned long ret_ip,
struct pt_regs *regs, void *data)
{ {
KUNIT_EXPECT_FALSE(current_test, preemptible()); KUNIT_EXPECT_FALSE(current_test, preemptible());
KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip); KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
......
...@@ -49,6 +49,7 @@ static void show_backtrace(void) ...@@ -49,6 +49,7 @@ static void show_backtrace(void)
} }
static int sample_entry_handler(struct fprobe *fp, unsigned long ip, static int sample_entry_handler(struct fprobe *fp, unsigned long ip,
unsigned long ret_ip,
struct pt_regs *regs, void *data) struct pt_regs *regs, void *data)
{ {
if (use_trace) if (use_trace)
...@@ -65,10 +66,11 @@ static int sample_entry_handler(struct fprobe *fp, unsigned long ip, ...@@ -65,10 +66,11 @@ static int sample_entry_handler(struct fprobe *fp, unsigned long ip,
return 0; return 0;
} }
static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs, static void sample_exit_handler(struct fprobe *fp, unsigned long ip,
unsigned long ret_ip, struct pt_regs *regs,
void *data) void *data)
{ {
unsigned long rip = instruction_pointer(regs); unsigned long rip = ret_ip;
if (use_trace) if (use_trace)
/* /*
......
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Generic dynamic event - add/remove probes with BTF arguments
# requires: dynamic_events "<argname>":README
KPROBES=
FPROBES=
if grep -qF "p[:[<group>/][<event>]] <place> [<args>]" README ; then
KPROBES=yes
fi
if grep -qF "f[:[<group>/][<event>]] <func-name>[%return] [<args>]" README ; then
FPROBES=yes
fi
if [ -z "$KPROBES" -a "$FPROBES" ] ; then
exit_unsupported
fi
echo 0 > events/enable
echo > dynamic_events
TP=kfree
if [ "$FPROBES" ] ; then
echo "f:fpevent $TP object" >> dynamic_events
echo "t:tpevent $TP ptr" >> dynamic_events
grep -q "fpevent.*object=object" dynamic_events
grep -q "tpevent.*ptr=ptr" dynamic_events
echo > dynamic_events
echo "f:fpevent $TP "'$arg1' >> dynamic_events
grep -q "fpevent.*object=object" dynamic_events
echo > dynamic_events
echo "f:fpevent $TP "'$arg*' >> dynamic_events
echo "t:tpevent $TP "'$arg*' >> dynamic_events
grep -q "fpevent.*object=object" dynamic_events
grep -q "tpevent.*ptr=ptr" dynamic_events
! grep -q "tpevent.*_data" dynamic_events
fi
echo > dynamic_events
if [ "$KPROBES" ] ; then
echo "p:kpevent $TP object" >> dynamic_events
grep -q "kpevent.*object=object" dynamic_events
echo > dynamic_events
echo "p:kpevent $TP "'$arg*' >> dynamic_events
grep -q "kpevent.*object=object" dynamic_events
fi
clear_trace
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Generic dynamic event - add/remove fprobe events
# requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README
echo 0 > events/enable
echo > dynamic_events
PLACE=$FUNCTION_FORK
echo "f:myevent1 $PLACE" >> dynamic_events
echo "f:myevent2 $PLACE%return" >> dynamic_events
grep -q myevent1 dynamic_events
grep -q myevent2 dynamic_events
test -d events/fprobes/myevent1
test -d events/fprobes/myevent2
echo "-:myevent2" >> dynamic_events
grep -q myevent1 dynamic_events
! grep -q myevent2 dynamic_events
echo > dynamic_events
clear_trace
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Generic dynamic event - add/remove tracepoint probe events
# requires: dynamic_events "t[:[<group>/][<event>]] <tracepoint> [<args>]":README
echo 0 > events/enable
echo > dynamic_events
TRACEPOINT1=kmem_cache_alloc
TRACEPOINT2=kmem_cache_free
echo "t:myevent1 $TRACEPOINT1" >> dynamic_events
echo "t:myevent2 $TRACEPOINT2" >> dynamic_events
grep -q myevent1 dynamic_events
grep -q myevent2 dynamic_events
test -d events/tracepoints/myevent1
test -d events/tracepoints/myevent2
echo "-:myevent2" >> dynamic_events
grep -q myevent1 dynamic_events
! grep -q myevent2 dynamic_events
echo > dynamic_events
clear_trace
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Fprobe event parser error log check
# requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README
check_error() { # command-with-error-pos-by-^
ftrace_errlog_check 'trace_fprobe' "$1" 'dynamic_events'
}
case `uname -m` in
x86_64|i[3456]86)
REG=%ax ;;
aarch64)
REG=%x0 ;;
*)
REG=%r0 ;;
esac
check_error 'f^100 vfs_read' # MAXACT_NO_KPROBE
check_error 'f^1a111 vfs_read' # BAD_MAXACT
check_error 'f^100000 vfs_read' # MAXACT_TOO_BIG
check_error 'f ^non_exist_func' # BAD_PROBE_ADDR (enoent)
check_error 'f ^vfs_read+10' # BAD_PROBE_ADDR
check_error 'f:^/bar vfs_read' # NO_GROUP_NAME
check_error 'f:^12345678901234567890123456789012345678901234567890123456789012345/bar vfs_read' # GROUP_TOO_LONG
check_error 'f:^foo.1/bar vfs_read' # BAD_GROUP_NAME
check_error 'f:^ vfs_read' # NO_EVENT_NAME
check_error 'f:foo/^12345678901234567890123456789012345678901234567890123456789012345 vfs_read' # EVENT_TOO_LONG
check_error 'f:foo/^bar.1 vfs_read' # BAD_EVENT_NAME
check_error 'f vfs_read ^$retval' # RETVAL_ON_PROBE
check_error 'f vfs_read ^$stack10000' # BAD_STACK_NUM
check_error 'f vfs_read ^$arg10000' # BAD_ARG_NUM
check_error 'f vfs_read ^$none_var' # BAD_VAR
check_error 'f vfs_read ^'$REG # BAD_VAR
check_error 'f vfs_read ^@12345678abcde' # BAD_MEM_ADDR
check_error 'f vfs_read ^@+10' # FILE_ON_KPROBE
grep -q "imm-value" README && \
check_error 'f vfs_read arg1=\^x' # BAD_IMM
grep -q "imm-string" README && \
check_error 'f vfs_read arg1=\"abcd^' # IMMSTR_NO_CLOSE
check_error 'f vfs_read ^+0@0)' # DEREF_NEED_BRACE
check_error 'f vfs_read ^+0ab1(@0)' # BAD_DEREF_OFFS
check_error 'f vfs_read +0(+0(@0^)' # DEREF_OPEN_BRACE
if grep -A1 "fetcharg:" README | grep -q '\$comm' ; then
check_error 'f vfs_read +0(^$comm)' # COMM_CANT_DEREF
fi
check_error 'f vfs_read ^&1' # BAD_FETCH_ARG
# We've introduced this limitation with array support
if grep -q ' <type>\\\[<array-size>\\\]' README; then
check_error 'f vfs_read +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
check_error 'f vfs_read +0(@11):u8[10^' # ARRAY_NO_CLOSE
check_error 'f vfs_read +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
check_error 'f vfs_read +0(@11):u8[^10a]' # BAD_ARRAY_NUM
check_error 'f vfs_read +0(@11):u8[^256]' # ARRAY_TOO_BIG
fi
check_error 'f vfs_read @11:^unknown_type' # BAD_TYPE
check_error 'f vfs_read $stack0:^string' # BAD_STRING
check_error 'f vfs_read @11:^b10@a/16' # BAD_BITFIELD
check_error 'f vfs_read ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_LOG
check_error 'f vfs_read ^=@11' # NO_ARG_NAME
check_error 'f vfs_read ^var.1=@11' # BAD_ARG_NAME
check_error 'f vfs_read var1=@11 ^var1=@12' # USED_ARG_NAME
check_error 'f vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
check_error 'f vfs_read arg1=^' # NO_ARG_BODY
# multiprobe errors
if grep -q "Create/append/" README && grep -q "imm-value" README; then
echo "f:fprobes/testevent $FUNCTION_FORK" > dynamic_events
check_error '^f:fprobes/testevent do_exit%return' # DIFF_PROBE_TYPE
# Explicitly use printf "%s" to not interpret \1
printf "%s" "f:fprobes/testevent $FUNCTION_FORK abcd=\\1" > dynamic_events
check_error "f:fprobes/testevent $FUNCTION_FORK ^bcd=\\1" # DIFF_ARG_TYPE
check_error "f:fprobes/testevent $FUNCTION_FORK ^abcd=\\1:u8" # DIFF_ARG_TYPE
check_error "f:fprobes/testevent $FUNCTION_FORK ^abcd=\\\"foo\"" # DIFF_ARG_TYPE
check_error "^f:fprobes/testevent $FUNCTION_FORK abcd=\\1" # SAME_PROBE
fi
# %return suffix errors
check_error 'f vfs_read^%hoge' # BAD_ADDR_SUFFIX
# BTF arguments errors
if grep -q "<argname>" README; then
check_error 'f vfs_read args=^$arg*' # BAD_VAR_ARGS
check_error 'f vfs_read +0(^$arg*)' # BAD_VAR_ARGS
check_error 'f vfs_read $arg* ^$arg*' # DOUBLE_ARGS
check_error 'f vfs_read%return ^$arg*' # NOFENTRY_ARGS
check_error 'f vfs_read ^hoge' # NO_BTFARG
check_error 'f kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
check_error 'f kfree%return ^$retval' # NO_RETVAL
else
check_error 'f vfs_read ^$arg*' # NOSUP_BTFARG
check_error 't kfree ^$arg*' # NOSUP_BTFARG
fi
exit 0
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Tracepoint probe event parser error log check
# requires: dynamic_events "t[:[<group>/][<event>]] <tracepoint> [<args>]":README
check_error() { # command-with-error-pos-by-^
ftrace_errlog_check 'trace_fprobe' "$1" 'dynamic_events'
}
check_error 't^100 kfree' # BAD_MAXACT_TYPE
check_error 't ^non_exist_tracepoint' # NO_TRACEPOINT
check_error 't:^/bar kfree' # NO_GROUP_NAME
check_error 't:^12345678901234567890123456789012345678901234567890123456789012345/bar kfree' # GROUP_TOO_LONG
check_error 't:^foo.1/bar kfree' # BAD_GROUP_NAME
check_error 't:^ kfree' # NO_EVENT_NAME
check_error 't:foo/^12345678901234567890123456789012345678901234567890123456789012345 kfree' # EVENT_TOO_LONG
check_error 't:foo/^bar.1 kfree' # BAD_EVENT_NAME
check_error 't kfree ^$retval' # RETVAL_ON_PROBE
check_error 't kfree ^$stack10000' # BAD_STACK_NUM
check_error 't kfree ^$arg10000' # BAD_ARG_NUM
check_error 't kfree ^$none_var' # BAD_VAR
check_error 't kfree ^%rax' # BAD_VAR
check_error 't kfree ^@12345678abcde' # BAD_MEM_ADDR
check_error 't kfree ^@+10' # FILE_ON_KPROBE
grep -q "imm-value" README && \
check_error 't kfree arg1=\^x' # BAD_IMM
grep -q "imm-string" README && \
check_error 't kfree arg1=\"abcd^' # IMMSTR_NO_CLOSE
check_error 't kfree ^+0@0)' # DEREF_NEED_BRACE
check_error 't kfree ^+0ab1(@0)' # BAD_DEREF_OFFS
check_error 't kfree +0(+0(@0^)' # DEREF_OPEN_BRACE
if grep -A1 "fetcharg:" README | grep -q '\$comm' ; then
check_error 't kfree +0(^$comm)' # COMM_CANT_DEREF
fi
check_error 't kfree ^&1' # BAD_FETCH_ARG
# We've introduced this limitation with array support
if grep -q ' <type>\\\[<array-size>\\\]' README; then
check_error 't kfree +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
check_error 't kfree +0(@11):u8[10^' # ARRAY_NO_CLOSE
check_error 't kfree +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
check_error 't kfree +0(@11):u8[^10a]' # BAD_ARRAY_NUM
check_error 't kfree +0(@11):u8[^256]' # ARRAY_TOO_BIG
fi
check_error 't kfree @11:^unknown_type' # BAD_TYPE
check_error 't kfree $stack0:^string' # BAD_STRING
check_error 't kfree @11:^b10@a/16' # BAD_BITFIELD
check_error 't kfree ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_LOG
check_error 't kfree ^=@11' # NO_ARG_NAME
check_error 't kfree ^var.1=@11' # BAD_ARG_NAME
check_error 't kfree var1=@11 ^var1=@12' # USED_ARG_NAME
check_error 't kfree ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
check_error 't kfree arg1=^' # NO_ARG_BODY
# multiprobe errors
if grep -q "Create/append/" README && grep -q "imm-value" README; then
echo "t:tracepoint/testevent kfree" > dynamic_events
check_error '^f:tracepoint/testevent kfree' # DIFF_PROBE_TYPE
# Explicitly use printf "%s" to not interpret \1
printf "%s" "t:tracepoints/testevent kfree abcd=\\1" > dynamic_events
check_error "t:tracepoints/testevent kfree ^bcd=\\1" # DIFF_ARG_TYPE
check_error "t:tracepoints/testevent kfree ^abcd=\\1:u8" # DIFF_ARG_TYPE
check_error "t:tracepoints/testevent kfree ^abcd=\\\"foo\"" # DIFF_ARG_TYPE
check_error "^t:tracepoints/testevent kfree abcd=\\1" # SAME_PROBE
fi
exit 0
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2023 Akanksha J N, IBM corporation
# description: Register multiple kprobe events in a function
# requires: kprobe_events
for i in `seq 0 255`; do
echo p $FUNCTION_FORK+${i} >> kprobe_events || continue
done
cat kprobe_events >> $testlog
echo 1 > events/kprobes/enable
( echo "forked" )
echo 0 > events/kprobes/enable
echo > kprobe_events
echo "Waiting for unoptimizing & freeing"
sleep 5
echo "Done"
...@@ -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
...@@ -103,4 +103,18 @@ check_error 'p vfs_read^%hoge' # BAD_ADDR_SUFFIX ...@@ -103,4 +103,18 @@ check_error 'p vfs_read^%hoge' # BAD_ADDR_SUFFIX
check_error 'p ^vfs_read+10%return' # BAD_RETPROBE check_error 'p ^vfs_read+10%return' # BAD_RETPROBE
fi fi
# BTF arguments errors
if grep -q "<argname>" README; then
check_error 'p vfs_read args=^$arg*' # BAD_VAR_ARGS
check_error 'p vfs_read +0(^$arg*)' # BAD_VAR_ARGS
check_error 'p vfs_read $arg* ^$arg*' # DOUBLE_ARGS
check_error 'r vfs_read ^$arg*' # NOFENTRY_ARGS
check_error 'p vfs_read+8 ^$arg*' # NOFENTRY_ARGS
check_error 'p vfs_read ^hoge' # NO_BTFARG
check_error 'p kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
check_error 'r kfree ^$retval' # NO_RETVAL
else
check_error 'p vfs_read ^$arg*' # NOSUP_BTFARG
fi
exit 0 exit 0
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