tracing: Creation of event probe
Steven Rostedt
maintainerv7 cover tracing: Creation of event probe
The first 5 patches of this series has had Masami's updates as well as his acks, and will no longer be in future series, as I'll be pushing them into my linux-next queue.
Patch 6 received an ack from Masami, but I updated it, by pulling more out of the old patch 7 and into that patch, adding more of the enum logic into the internal processing of the set_print_fmt() code. This way, it adds the switch statement, such that the addition of the eprobe only needs to add a new case.
I pulled out the code from the old patch 7 that converts the parameter for process_fetch_insn() regs into a void pointer called rec. I figured that should be a separate patch as it's more set up for the eprobe code, then having to do with the eprobe code itself.
For the eprobe code, I incorporated more of Masami's comments as well as found some minor bugs. Those updates were:
Masami's suggestions:
- Remove "return" for void function calls in trace_event_put_ref()
- Used bit mask over checking separate bits of flags one at a time.
- Check for duplicate eprobes.
Fixes:
- Check for NULL pointer for probe_cleanup as there was an error path that could call it with a NULL pointer.
- Check on create that it's for an eprobe (check for 'e'). Was breaking updates to kprobes and uprobes that were added by dynamic_events.
- Add paranoid check of by enum to print_fmt call.
- Fixed error_log ^ index handling
Other updates:
- Updated the README to include eprobes
- Added clear_dynamic_events() to selftests, to remove them even when there's a dependency between them.
- Added a selftest to test the eprobes.
Steven Rostedt (VMware) (9):
tracing: Add DYNAMIC flag for dynamic events
tracing: Have dynamic events have a ref counter tracing/probe: Have traceprobe_parse_probe_arg() take a const arg tracing/probes: Allow for dot delimiter as well as slash for system names tracing/probes: Use struct_size() instead of defining custom macros tracing/probe: Change traceprobe_set_print_fmt() to take a type tracing/probes: Have process_fetch_insn() take a void * instead of pt_regs selftests/ftrace: Add clear_dynamic_events() to test cases selftests/ftrace: Add selftest for testing eprobe events
Tzvetomir Stoyanov (VMware) (1):
tracing: Add a probe that attaches to trace events
----
include/linux/trace_events.h | 52 +- kernel/trace/Makefile | 1 + kernel/trace/trace.c | 9 +- kernel/trace/trace.h | 18 + kernel/trace/trace_dynevent.c | 38 + kernel/trace/trace_dynevent.h | 4 +- kernel/trace/trace_eprobe.c | 932 +++++++++++++++++++++ kernel/trace/trace_event_perf.c | 6 +- kernel/trace/trace_events.c | 22 +- kernel/trace/trace_events_synth.c | 21 +- kernel/trace/trace_events_trigger.c | 20 +- kernel/trace/trace_kprobe.c | 43 +- kernel/trace/trace_probe.c | 84 +- kernel/trace/trace_probe.h | 15 +- kernel/trace/trace_probe_tmpl.h | 6 +- kernel/trace/trace_uprobe.c | 34 +- .../ftrace/test.d/dynevent/add_remove_eprobe.tc | 53 ++ tools/testing/selftests/ftrace/test.d/functions | 22 + 18 files changed, 1275 insertions(+), 105 deletions(-) create mode 100644 kernel/trace/trace_eprobe.c create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
Steven Rostedt
maintainerPATCH v7 1/10 tracing: Add DYNAMIC flag for dynamic events
From: "Steven Rostedt (VMware)" <[email protected]>
To differentiate between static and dynamic events, add a new flag DYNAMIC to the event flags that all dynamic events have set. This will allow to differentiate when attaching to a dynamic event from a static event.
Static events have a mod pointer that references the module they were created in (or NULL for core kernel). This can be incremented when the event has something attached to it. But there exists no such mechanism for dynamic events. This is dangerous as the dynamic events may now disappear without the "attachment" knowing that it no longer exists.
To enforce the dynamic flag, change dyn_event_add() to pass the event that is being created such that it can set the DYNAMIC flag of the event. This helps make sure that no location that creates a dynamic event misses setting this flag.
--- include/linux/trace_events.h | 3 +++ kernel/trace/trace_dynevent.h | 4 +++- kernel/trace/trace_events_synth.c | 2 +- kernel/trace/trace_kprobe.c | 4 ++-- kernel/trace/trace_uprobe.c | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-)
include/linux/trace_events.h +3 -0
@@ -310,6 +310,7 @@ enum {TRACE_EVENT_FL_NO_SET_FILTER_BIT ,TRACE_EVENT_FL_IGNORE_ENABLE_BIT ,TRACE_EVENT_FL_TRACEPOINT_BIT , +TRACE_EVENT_FL_DYNAMIC_BIT ,TRACE_EVENT_FL_KPROBE_BIT , TRACE_EVENT_FL_UPROBE_BIT, };
@@ -321,6 +322,7 @@ enum {* NO_SET_FILTER - Set when filter has error and is to be ignored * IGNORE_ENABLE - For trace internal events, do not enable with debugfs file * TRACEPOINT - Event is a tracepoint +* DYNAMIC - Event is a dynamic event (created at run time) * KPROBE - Event is a kprobe * UPROBE - Event is a uprobe */
@@ -330,6 +332,7 @@ enum {TRACE_EVENT_FL_NO_SET_FILTER = (1 <<TRACE_EVENT_FL_NO_SET_FILTER_BIT ),TRACE_EVENT_FL_IGNORE_ENABLE = (1 <<TRACE_EVENT_FL_IGNORE_ENABLE_BIT ),TRACE_EVENT_FL_TRACEPOINT = (1 <<TRACE_EVENT_FL_TRACEPOINT_BIT ), +TRACE_EVENT_FL_DYNAMIC = (1 <<TRACE_EVENT_FL_DYNAMIC_BIT ),TRACE_EVENT_FL_KPROBE = (1 <<TRACE_EVENT_FL_KPROBE_BIT ), TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT), };
kernel/trace/trace_dynevent.h +3 -1
@@ -76,13 +76,15 @@ int dyn_event_init(struct dyn_event *ev, struct dyn_event_operations *ops)return 0 ; } -static inline int dyn_event_add (struct dyn_event *ev) +static inline int dyn_event_add (struct dyn_event *ev, +struct trace_event_call *call) {lockdep_assert_held (&event_mutex);if (!ev || !ev->ops)return -EINVAL ; + call->flags |=TRACE_EVENT_FL_DYNAMIC ;list_add_tail (&ev->list, &dyn_event_list);return 0 ; }
kernel/trace/trace_events_synth.c +1 -1
@@ -1298,7 +1298,7 @@ static int __create_synth_event(const char *name, const char *raw_fields) } ret =register_synth_event (event);if (!ret) -dyn_event_add (&event->devent); +dyn_event_add (&event->devent, &event->call);else free_synth_event (event); out:
kernel/trace/trace_kprobe.c +2 -2
@@ -618,7 +618,7 @@ static int append_trace_kprobe(struct trace_kprobe *tk, struct trace_kprobe *to)if (ret)trace_probe_unlink (&tk->tp);else -dyn_event_add (&tk->devent); +dyn_event_add (&tk->devent,trace_probe_event_call (&tk->tp));return ret; }
@@ -661,7 +661,7 @@ static int register_trace_kprobe(struct trace_kprobe *tk)if (ret <0 )unregister_kprobe_event (tk);else -dyn_event_add (&tk->devent); +dyn_event_add (&tk->devent,trace_probe_event_call (&tk->tp)); end:mutex_unlock (&event_mutex);
kernel/trace/trace_uprobe.c +2 -2
@@ -455,7 +455,7 @@ static int append_trace_uprobe(struct trace_uprobe *tu, struct trace_uprobe *to)/* Append to existing event */ ret =trace_probe_append (&tu->tp, &to->tp);if (!ret) -dyn_event_add (&tu->devent); +dyn_event_add (&tu->devent,trace_probe_event_call (&tu->tp));return ret; }
@@ -518,7 +518,7 @@ static int register_trace_uprobe(struct trace_uprobe *tu) goto end; } -dyn_event_add (&tu->devent); +dyn_event_add (&tu->devent,trace_probe_event_call (&tu->tp)); end:mutex_unlock (&event_mutex);
Signature · 1 line
2.30.2
Steven Rostedt
maintainerPATCH v7 2/10 tracing: Have dynamic events have a ref counter
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
As dynamic events are not created by modules, if something is attached to one, calling "try_module_get()" on its "mod" field, is not going to keep the dynamic event from going away.
Since dynamic events do not need the "mod" pointer of the event structure, make a union out of it in order to save memory (there's one structure for each of the thousand+ events in the kernel), and have any event with the DYNAMIC flag set to use a ref counter instead.
--- include/linux/trace_events.h | 45 ++++++++++++++++++++++++++++- kernel/trace/trace.c | 4 +-- kernel/trace/trace_dynevent.c | 38 ++++++++++++++++++++++++ kernel/trace/trace_event_perf.c | 6 ++-- kernel/trace/trace_events.c | 22 +++++++++----- kernel/trace/trace_events_synth.c | 19 +++++++----- kernel/trace/trace_events_trigger.c | 6 ++-- kernel/trace/trace_kprobe.c | 4 +++ kernel/trace/trace_uprobe.c | 4 +++ 9 files changed, 124 insertions(+), 24 deletions(-)
include/linux/trace_events.h +44 -1
@@ -350,7 +350,14 @@ struct trace_event_call {struct trace_event event;char *print_fmt;struct event_filter *filter; -void *mod; +/* +* Static events can disappear with modules, +* where as dynamic ones need their own ref count. +*/ +union { +void *module; +atomic_t refcnt; + };void *data;/* See the TRACE_EVENT_FL_* flags above */
@@ -366,6 +373,42 @@ struct trace_event_call {#endif }; +#ifdef CONFIG_DYNAMIC_EVENTS +bool trace_event_dyn_try_get_ref (struct trace_event_call *call); +void trace_event_dyn_put_ref (struct trace_event_call *call); +bool trace_event_dyn_busy (struct trace_event_call *call); +#else +static inline bool trace_event_dyn_try_get_ref (struct trace_event_call *call) +{ +/* Without DYNAMIC_EVENTS configured, nothing should be calling this */ +return false; +} +static inline void trace_event_dyn_put_ref (struct trace_event_call *call) +{ +} +static inline bool trace_event_dyn_busy (struct trace_event_call *call) +{ +/* Nothing should call this without DYNAIMIC_EVENTS configured. */ +return true; +} +#endif + +static inline bool trace_event_try_get_ref (struct trace_event_call *call) +{ +if (call->flags &TRACE_EVENT_FL_DYNAMIC ) +return trace_event_dyn_try_get_ref (call); +else +return try_module_get (call->module); +} + +static inline void trace_event_put_ref (struct trace_event_call *call) +{ +if (call->flags &TRACE_EVENT_FL_DYNAMIC ) +trace_event_dyn_put_ref (call); +else +module_put (call->module); +} +#ifdef CONFIG_PERF_EVENTS static inline bool bpf_prog_array_valid (struct trace_event_call *call) {
kernel/trace/trace.c +2 -2
@@ -3697,11 +3697,11 @@ static bool trace_safe_str(struct trace_iterator *iter, const char *str)return false; event = container_of(trace_event, struct trace_event_call, event); -if (!event->mod) +if ((event->flags &TRACE_EVENT_FL_DYNAMIC ) || !event->module)return false;/* Would rather have rodata, but this will suffice */ -if (within_module_core (addr, event->mod)) +if (within_module_core (addr, event->module))return true;return false;
kernel/trace/trace_dynevent.c +38 -0
@@ -13,11 +13,49 @@#include <linux/tracefs.h> #include "trace.h" +#include "trace_output.h" /* for trace_event_sem */ #include "trace_dynevent.h" static DEFINE_MUTEX (dyn_event_ops_mutex);static LIST_HEAD (dyn_event_ops_list); +bool trace_event_dyn_try_get_ref (struct trace_event_call *dyn_call) +{ +struct trace_event_call *call; +bool ret = false; + +if (WARN_ON_ONCE (!(dyn_call->flags &TRACE_EVENT_FL_DYNAMIC ))) +return false; + +down_read (&trace_event_sem); +list_for_each_entry (call, &ftrace_events, list) { +if (call == dyn_call) { +atomic_inc (&dyn_call->refcnt); + ret = true; + } + } +up_read (&trace_event_sem); +return ret; +} + +void trace_event_dyn_put_ref (struct trace_event_call *call) +{ +if (WARN_ON_ONCE (!(call->flags &TRACE_EVENT_FL_DYNAMIC ))) +return ; + +if (WARN_ON_ONCE (atomic_read (&call->refcnt) <=0 )) { +atomic_set (&call->refcnt,0 ); +return ; + } + +atomic_dec (&call->refcnt); +} + +bool trace_event_dyn_busy (struct trace_event_call *call) +{ +return atomic_read (&call->refcnt) !=0 ; +} +int dyn_event_register (struct dyn_event_operations *ops) { if (!ops || !ops->create || !ops->show || !ops->is_busy ||
kernel/trace/trace_event_perf.c +3 -3
@@ -177,7 +177,7 @@ static void perf_trace_event_unreg(struct perf_event *p_event) } } out: -module_put (tp_event->mod); +trace_event_put_ref (tp_event); }static int perf_trace_event_open (struct perf_event *p_event)
@@ -224,10 +224,10 @@ int perf_trace_init(struct perf_event *p_event)list_for_each_entry (tp_event, &ftrace_events, list) {if (tp_event->event.type == event_id && tp_event->class && tp_event->class->reg && -try_module_get (tp_event->mod)) { +trace_event_try_get_ref (tp_event)) { ret =perf_trace_event_init (tp_event, p_event);if (ret) -module_put (tp_event->mod); +trace_event_put_ref (tp_event);break ; } }
kernel/trace/trace_events.c +14 -8
@@ -2525,7 +2525,10 @@ __register_event(struct trace_event_call *call, struct module *mod)return ret;list_add (&call->list, &ftrace_events); - call->mod = mod; +if (call->flags &TRACE_EVENT_FL_DYNAMIC ) +atomic_set (&call->refcnt,0 ); +else + call->module = mod;return 0 ; }
@@ -2839,7 +2842,9 @@ static void trace_module_remove_events(struct module *mod)down_write (&trace_event_sem);list_for_each_entry_safe (call, p, &ftrace_events, list) { -if (call->mod == mod) +if ((call->flags &TRACE_EVENT_FL_DYNAMIC ) || !call->module) +continue ; +if (call->module == mod)__trace_remove_event_call (call); }up_write (&trace_event_sem);
@@ -2982,7 +2987,7 @@ struct trace_event_file *trace_get_event_file(const char *instance, }/* Don't let event modules unload while in use */ - ret =try_module_get (file->event_call->mod); + ret =trace_event_try_get_ref (file->event_call);if (!ret) {trace_array_put (tr); ret = -EBUSY ;
@@ -3012,7 +3017,7 @@ EXPORT_SYMBOL_GPL(trace_get_event_file);void trace_put_event_file (struct trace_event_file *file) {mutex_lock (&event_mutex); -module_put (file->event_call->mod); +trace_event_put_ref (file->event_call);mutex_unlock (&event_mutex);trace_array_put (file->tr);
@@ -3147,7 +3152,7 @@ static int free_probe_data(void *data)if (!edata->ref) {/* Remove the SOFT_MODE flag */ __ftrace_event_enable_disable (edata->file,0 ,1 ); -module_put (edata->file->event_call->mod); +trace_event_put_ref (edata->file->event_call);kfree (edata); }return 0 ;
@@ -3280,7 +3285,7 @@ event_enable_func(struct trace_array *tr, struct ftrace_hash *hash, out_reg:/* Don't let event modules unload while probe registered */ - ret =try_module_get (file->event_call->mod); + ret =trace_event_try_get_ref (file->event_call);if (!ret) { ret = -EBUSY ; goto out_free;
@@ -3310,7 +3315,7 @@ event_enable_func(struct trace_array *tr, struct ftrace_hash *hash, out_disable:__ftrace_event_enable_disable (file,0 ,1 ); out_put: -module_put (file->event_call->mod); +trace_event_put_ref (file->event_call); out_free:kfree (data); goto out;
@@ -3376,7 +3381,8 @@ void __trace_early_add_events(struct trace_array *tr)list_for_each_entry (call, &ftrace_events, list) {/* Early boot up should not have any modules loaded */ -if (WARN_ON_ONCE (call->mod)) +if (!(call->flags &TRACE_EVENT_FL_DYNAMIC ) && +WARN_ON_ONCE (call->module))continue ; ret =__trace_early_add_new_event (call, tr);
kernel/trace/trace_events_synth.c +12 -7
@@ -1369,13 +1369,15 @@ static int destroy_synth_event(struct synth_event *se)int ret;if (se->ref) - ret = -EBUSY ; -else { - ret =unregister_synth_event (se); -if (!ret) { -dyn_event_remove (&se->devent); -free_synth_event (se); - } +return -EBUSY ; + +if (trace_event_dyn_busy (&se->call)) +return -EBUSY ; + + ret =unregister_synth_event (se); +if (!ret) { +dyn_event_remove (&se->devent); +free_synth_event (se); }return ret;
@@ -2102,6 +2104,9 @@ static int synth_event_release(struct dyn_event *ev)if (event->ref)return -EBUSY ; +if (trace_event_dyn_busy (&event->call)) +return -EBUSY ; + ret =unregister_synth_event (event);if (ret)return ret;
kernel/trace/trace_events_trigger.c +3 -3
@@ -1334,7 +1334,7 @@ void event_enable_trigger_free(struct event_trigger_ops *ops,if (!data->ref) {/* Remove the SOFT_MODE flag */ trace_event_enable_disable (enable_data->file,0 ,1 ); -module_put (enable_data->file->event_call->mod); +trace_event_put_ref (enable_data->file->event_call);trigger_data_free (data);kfree (enable_data); }
@@ -1481,7 +1481,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, out_reg:/* Don't let event modules unload while probe registered */ - ret =try_module_get (event_enable_file->event_call->mod); + ret =trace_event_try_get_ref (event_enable_file->event_call);if (!ret) { ret = -EBUSY ; goto out_free;
@@ -1510,7 +1510,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, out_disable:trace_event_enable_disable (event_enable_file,0 ,1 ); out_put: -module_put (event_enable_file->event_call->mod); +trace_event_put_ref (event_enable_file->event_call); out_free:if (cmd_ops->set_filter) cmd_ops->set_filter (NULL , trigger_data,NULL );
kernel/trace/trace_kprobe.c +4 -0
@@ -543,6 +543,10 @@ static int unregister_trace_kprobe(struct trace_kprobe *tk)if (trace_probe_is_enabled (&tk->tp))return -EBUSY ; +/* If there's a reference to the dynamic event */ +if (trace_event_dyn_busy (trace_probe_event_call (&tk->tp))) +return -EBUSY ; +/* Will fail if probe is being used by ftrace or perf */ if (unregister_kprobe_event (tk))return -EBUSY ;
kernel/trace/trace_uprobe.c +4 -0
@@ -393,6 +393,10 @@ static int unregister_trace_uprobe(struct trace_uprobe *tu)if (trace_probe_has_sibling (&tu->tp)) goto unreg; +/* If there's a reference to the dynamic event */ +if (trace_event_dyn_busy (trace_probe_event_call (&tu->tp))) +return -EBUSY ; + ret =unregister_uprobe_event (tu);if (ret)return ret;
Signature · 1 line
2.30.2
Steven Rostedt
maintainerPATCH v7 3/10 tracing/probe: Have traceprobe_parse_probe_arg() take a const arg
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The two places that call traceprobe_parse_probe_arg() allocate a temporary buffer to copy the argv[i] into, because argv[i] is constant and the traceprobe_parse_probe_arg() will modify it to do the parsing. These two places allocate this buffer and then free it right after calling this function, leaving the onus of this allocation to the caller.
As there's about to be a third user of this function that will have to do the same thing, instead of having the caller allocate the temporary buffer, simply move that allocation into the traceprobe_parse_probe_arg() itself, which will simplify the code of the callers.
--- kernel/trace/trace_kprobe.c | 9 +------ kernel/trace/trace_probe.c | 47 ++++++++++++++++++++++--------------- kernel/trace/trace_probe.h | 2 +- kernel/trace/trace_uprobe.c | 9 +------ 4 files changed, 31 insertions(+), 36 deletions(-)
kernel/trace/trace_kprobe.c +1 -8
@@ -873,15 +873,8 @@ static int __trace_kprobe_create(int argc, const char *argv[])/* parse arguments */ for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { - tmp =kstrdup (argv[i],GFP_KERNEL ); -if (!tmp) { - ret = -ENOMEM ; - goto error; - } -trace_probe_log_set_index (i +2 ); - ret =traceprobe_parse_probe_arg (&tk->tp, i, tmp, flags); -kfree (tmp); + ret =traceprobe_parse_probe_arg (&tk->tp, i, argv[i], flags);if (ret) goto error;/* This can be -ENOMEM */ }
kernel/trace/trace_probe.c +28 -19
174 lines not on this page · raw message
kernel/trace/trace_probe.h +1 -1
@@ -354,7 +354,7 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char#define TPARG_FL_MASK GENMASK(2, 0)extern int traceprobe_parse_probe_arg (struct trace_probe *tp,int i, -char *arg,unsigned int flags); +const char *argv,unsigned int flags);extern int traceprobe_update_arg (struct probe_arg *arg);extern void traceprobe_free_probe_arg (struct probe_arg *arg);
kernel/trace/trace_uprobe.c +1 -8
@@ -684,16 +684,9 @@ static int __trace_uprobe_create(int argc, const char **argv)/* parse arguments */ for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { - tmp =kstrdup (argv[i],GFP_KERNEL ); -if (!tmp) { - ret = -ENOMEM ; - goto error; - } -trace_probe_log_set_index (i +2 ); - ret =traceprobe_parse_probe_arg (&tu->tp, i, tmp, + ret =traceprobe_parse_probe_arg (&tu->tp, i, argv[i], is_return ?TPARG_FL_RETURN :0 ); -kfree (tmp);if (ret) goto error; }
Signature · 1 line
2.30.2
Steven Rostedt
maintainerPATCH v7 4/10 tracing/probes: Allow for dot delimiter as well as slash for system names
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Kprobe and uprobe events can add a "system" to the events that are created via the kprobe_events and uprobe_events files respectively. If they do not include a "system" in the name, then the default "kprobes" or "uprobes" is used. The current notation to specify a system for one of these probe events is to add a '/' delimiter in the name, where the content before the '/' will be the system to use, and the content after will be the event name.
echo 'p:my_system/my_event' > kprobe_events
But this is inconsistent with the way histogram triggers separate their system / event names. The histogram triggers use a '.' delimiter, which can be confusing.
To allow this to be more consistent, as well as keep backward compatibility, allow the kprobe and uprobe events to denote a system name with either a '/' or a '.'.
That is:
echo 'p:my_system/my_event' > kprobe_events
is equivalent to:
echo 'p:my_system.my_event' > kprobe_events
--- kernel/trace/trace_probe.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/trace/trace_probe.c +3 -0
@@ -233,6 +233,9 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup,int len; slash =strchr (event,'/' ); +if (!slash) + slash =strchr (event,'.' ); +if (slash) {if (slash == event) {trace_probe_log_err (offset,NO_GROUP_NAME );
Signature · 1 line
2.30.2
Steven Rostedt
maintainerPATCH v7 5/10 tracing/probes: Use struct_size() instead of defining custom macros
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Remove SIZEOF_TRACE_KPROBE() and SIZEOF_TRACE_UPROBE() and use struct_size() as that's what it is made for. No need to have custom macros. Especially since struct_size() has some extra memory checks for correctness.
--- kernel/trace/trace_kprobe.c | 6 +----- kernel/trace/trace_uprobe.c | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-)
kernel/trace/trace_kprobe.c +1 -5
@@ -80,10 +80,6 @@ static struct trace_kprobe *to_trace_kprobe(struct dyn_event *ev)for_each_dyn_event (dpos) \ if (is_trace_kprobe(dpos) && (pos = to_trace_kprobe(dpos))) -#define SIZEOF_TRACE_KPROBE (n) \ - (offsetof(struct trace_kprobe, tp.args) + \ - (sizeof(struct probe_arg) * (n))) - static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk) {return tk->rp.handler !=NULL ;
@@ -265,7 +261,7 @@ static struct trace_kprobe *alloc_trace_kprobe(const char *group,struct trace_kprobe *tk;int ret = -ENOMEM ; - tk =kzalloc (SIZEOF_TRACE_KPROBE (nargs),GFP_KERNEL ); + tk =kzalloc (struct_size (tk, tp.args, nargs),GFP_KERNEL );if (!tk)return ERR_PTR (ret);
kernel/trace/trace_uprobe.c +1 -5
@@ -83,10 +83,6 @@ static struct trace_uprobe *to_trace_uprobe(struct dyn_event *ev) for_each_dyn_event(dpos) \ if (is_trace_uprobe(dpos) && (pos = to_trace_uprobe(dpos))) -#define SIZEOF_TRACE_UPROBE (n) \ - (offsetof(struct trace_uprobe, tp.args) + \ - (sizeof(struct probe_arg) * (n))) - static int register_uprobe_event(struct trace_uprobe *tu);static int unregister_uprobe_event (struct trace_uprobe *tu);
@@ -340,7 +336,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)struct trace_uprobe *tu;int ret; - tu =kzalloc (SIZEOF_TRACE_UPROBE (nargs),GFP_KERNEL ); + tu =kzalloc (struct_size (tu, tp.args, nargs),GFP_KERNEL );if (!tu)return ERR_PTR (-ENOMEM );
Signature · 1 line
2.30.2
Steven Rostedt
maintainerPATCH v7 6/10 tracing/probe: Change traceprobe_set_print_fmt() to take a type
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Instead of a boolean "is_return" have traceprobe_set_print_fmt() take a type (currently just PROBE_PRINT_NORMAL and PROBE_PRINT_RETURN). This will simplify adding different types. For example, the development of the event_probe, will need its own type as it prints an event, and not an IP.
---
Changes since v6:
- Incorporated more of the ptype enum further into the functions.
kernel/trace/trace_kprobe.c | 9 +++++++-- kernel/trace/trace_probe.c | 18 ++++++++++++------ kernel/trace/trace_probe.h | 7 ++++++- kernel/trace/trace_uprobe.c | 8 ++++++-- 4 files changed, 31 insertions(+), 11 deletions(-)
kernel/trace/trace_kprobe.c +7 -2
@@ -742,6 +742,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])bool is_return = false;char *symbol =NULL , *tmp =NULL ;const char *event =NULL , *group =KPROBE_EVENT_SYSTEM ; +enum probe_print_type ptype;int maxactive =0 ;long offset =0 ;void *addr =NULL ;
@@ -875,7 +876,8 @@ static int __trace_kprobe_create(int argc, const char *argv[]) goto error;/* This can be -ENOMEM */ } - ret =traceprobe_set_print_fmt (&tk->tp, is_return); + ptype = is_return ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; + ret =traceprobe_set_print_fmt (&tk->tp, ptype);if (ret <0 ) goto error;
@@ -1799,6 +1801,7 @@ struct trace_event_call * create_local_trace_kprobe(char *func, void *addr, unsigned long offs, bool is_return) { +enum probe_print_type ptype;struct trace_kprobe *tk;int ret;char *event;
@@ -1822,7 +1825,9 @@ create_local_trace_kprobe(char *func, void *addr, unsigned long offs,init_trace_event_call (tk); -if (traceprobe_set_print_fmt (&tk->tp,trace_kprobe_is_return (tk)) <0 ) { + ptype =trace_kprobe_is_return (tk) ? +PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; +if (traceprobe_set_print_fmt (&tk->tp, ptype) <0 ) { ret = -ENOMEM ; goto error; }
kernel/trace/trace_probe.c +12 -6
@@ -851,19 +851,25 @@ int traceprobe_update_arg(struct probe_arg *arg)/* When len=0, we just calculate the needed length */ #define LEN_OR_ZERO (len ? len - pos : 0)static int __set_print_fmt (struct trace_probe *tp,char *buf,int len, -bool is_return) +enum probe_print_type ptype) {struct probe_arg *parg;int i, j;int pos =0 ;const char *fmt, *arg; -if (!is_return) { +switch (ptype) { +case PROBE_PRINT_NORMAL : fmt ="(%lx)" ; arg ="REC->" FIELD_STRING_IP ; - }else { +break ; +case PROBE_PRINT_RETURN : fmt ="(%lx <- %lx)" ; arg ="REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP ; +break ; +default : +WARN_ON_ONCE (1 ); +return 0 ; } pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"%s" , fmt);
@@ -912,20 +918,20 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, }#undef LEN_OR_ZERO -int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return) +int traceprobe_set_print_fmt (struct trace_probe *tp,enum probe_print_type ptype) {struct trace_event_call *call =trace_probe_event_call (tp);int len;char *print_fmt;/* First: called with 0 length to calculate the needed length */ - len =__set_print_fmt (tp,NULL ,0 , is_return); + len =__set_print_fmt (tp,NULL ,0 , ptype); print_fmt =kmalloc (len +1 ,GFP_KERNEL );if (!print_fmt)return -ENOMEM ;/* Second: actually write the @print_fmt */ -__set_print_fmt (tp, print_fmt, len +1 , is_return); +__set_print_fmt (tp, print_fmt, len +1 , ptype); call->print_fmt = print_fmt;return 0 ;
kernel/trace/trace_probe.h +6 -1
@@ -363,7 +363,12 @@ extern int traceprobe_split_symbol_offset(char *symbol, long *offset);int traceprobe_parse_event_name (const char **pevent,const char **pgroup,char *buf,int offset); -extern int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return); +enum probe_print_type { +PROBE_PRINT_NORMAL , +PROBE_PRINT_RETURN , +}; + +extern int traceprobe_set_print_fmt (struct trace_probe *tp,enum probe_print_type ptype);#ifdef CONFIG_PERF_EVENTS extern struct trace_event_call *
kernel/trace/trace_uprobe.c +6 -2
@@ -536,6 +536,7 @@ static int __trace_uprobe_create(int argc, const char **argv)const char *event =NULL , *group =UPROBE_EVENT_SYSTEM ;char *arg, *filename, *rctr, *rctr_end, *tmp;char buf[MAX_EVENT_NAME_LEN ]; +enum probe_print_type ptype;struct path path;unsigned long offset, ref_ctr_offset;bool is_return = false;
@@ -687,7 +688,8 @@ static int __trace_uprobe_create(int argc, const char **argv) goto error; } - ret =traceprobe_set_print_fmt (&tu->tp,is_ret_probe (tu)); + ptype =is_ret_probe (tu) ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; + ret =traceprobe_set_print_fmt (&tu->tp, ptype);if (ret <0 ) goto error;
@@ -1578,6 +1580,7 @@ struct trace_event_call * create_local_trace_uprobe(char *name, unsigned long offs, unsigned long ref_ctr_offset, bool is_return) { +enum probe_print_type ptype;struct trace_uprobe *tu;struct path path;int ret;
@@ -1612,7 +1615,8 @@ create_local_trace_uprobe(char *name, unsigned long offs, tu->filename =kstrdup (name,GFP_KERNEL );init_trace_event_call (tu); -if (traceprobe_set_print_fmt (&tu->tp,is_ret_probe (tu)) <0 ) { + ptype =is_ret_probe (tu) ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; +if (traceprobe_set_print_fmt (&tu->tp, ptype) <0 ) { ret = -ENOMEM ; goto error; }
Signature · 1 line
2.30.2
Re: [PATCH v7 06/10] tracing/probe: Change traceprobe_set_print_fmt() to take a type
Quoted 10 lines · On Thu, 19 Aug 2021 00:13:27 -0400 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
Instead of a boolean "
is_return" havetraceprobe_set_print_fmt()take a type (currently justPROBE_PRINT_NORMALandPROBE_PRINT_RETURN). This will simplify adding different types. For example, the development of theevent_probe, will need its own type as it prints an event, and not an IP.Signed-off-by Steven Rostedt (VMware) <[email protected]>
Looks good to me.
Thanks!
Quoted 169 lines
---
Changes since v6:- Incorporated more of the ptype enum further into the functions.
kernel/trace/trace_kprobe.c | 9 +++++++-- kernel/trace/trace_probe.c | 18 ++++++++++++------ kernel/trace/trace_probe.h | 7 ++++++- kernel/trace/trace_uprobe.c | 8 ++++++-- 4 files changed, 31 insertions(+), 11 deletions(-)kernel/trace/trace_kprobe.c +7
-2@@ -742,6 +742,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])bool is_return = false;char *symbol =NULL , *tmp =NULL ;const char *event =NULL , *group =KPROBE_EVENT_SYSTEM ; +enum probe_print_type ptype;int maxactive =0 ;long offset =0 ;void *addr =NULL ;@@ -875,7 +876,8 @@ static int __trace_kprobe_create(int argc, const char *argv[]) goto error;/* This can be -ENOMEM */ } - ret =traceprobe_set_print_fmt (&tk->tp, is_return); + ptype = is_return ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; + ret =traceprobe_set_print_fmt (&tk->tp, ptype);if (ret <0 ) goto error;@@ -1799,6 +1801,7 @@ struct trace_event_call * create_local_trace_kprobe(char *func, void *addr, unsigned long offs, bool is_return) { +enum probe_print_type ptype;struct trace_kprobe *tk;int ret;char *event;@@ -1822,7 +1825,9 @@ create_local_trace_kprobe(char *func, void *addr, unsigned long offs,init_trace_event_call (tk); -if (traceprobe_set_print_fmt (&tk->tp,trace_kprobe_is_return (tk)) <0 ) { + ptype =trace_kprobe_is_return (tk) ? +PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; +if (traceprobe_set_print_fmt (&tk->tp, ptype) <0 ) { ret = -ENOMEM ; goto error; }kernel/trace/trace_probe.c +12
-6@@ -851,19 +851,25 @@ int traceprobe_update_arg(struct probe_arg *arg)/* When len=0, we just calculate the needed length */ #define LEN_OR_ZERO (len ? len - pos : 0)static int __set_print_fmt (struct trace_probe *tp,char *buf,int len, -bool is_return) +enum probe_print_type ptype) {struct probe_arg *parg;int i, j;int pos =0 ;const char *fmt, *arg; -if (!is_return) { +switch (ptype) { +case PROBE_PRINT_NORMAL : fmt ="(%lx)" ; arg ="REC->" FIELD_STRING_IP ; - }else { +break ; +case PROBE_PRINT_RETURN : fmt ="(%lx <- %lx)" ; arg ="REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP ; +break ; +default : +WARN_ON_ONCE (1 ); +return 0 ; } pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"%s" , fmt);@@ -912,20 +918,20 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, }#undef LEN_OR_ZERO -int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return) +int traceprobe_set_print_fmt (struct trace_probe *tp,enum probe_print_type ptype) {struct trace_event_call *call =trace_probe_event_call (tp);int len;char *print_fmt;/* First: called with 0 length to calculate the needed length */ - len =__set_print_fmt (tp,NULL ,0 , is_return); + len =__set_print_fmt (tp,NULL ,0 , ptype); print_fmt =kmalloc (len +1 ,GFP_KERNEL );if (!print_fmt)return -ENOMEM ;/* Second: actually write the @print_fmt */ -__set_print_fmt (tp, print_fmt, len +1 , is_return); +__set_print_fmt (tp, print_fmt, len +1 , ptype); call->print_fmt = print_fmt;return 0 ;kernel/trace/trace_probe.h +6
-1@@ -363,7 +363,12 @@ extern int traceprobe_split_symbol_offset(char *symbol, long *offset);int traceprobe_parse_event_name (const char **pevent,const char **pgroup,char *buf,int offset); -extern int traceprobe_set_print_fmt (struct trace_probe *tp,bool is_return); +enum probe_print_type { +PROBE_PRINT_NORMAL , +PROBE_PRINT_RETURN , +}; + +extern int traceprobe_set_print_fmt (struct trace_probe *tp,enum probe_print_type ptype);#ifdef CONFIG_PERF_EVENTS extern struct trace_event_call *kernel/trace/trace_uprobe.c +6
-2@@ -536,6 +536,7 @@ static int __trace_uprobe_create(int argc, const char **argv)const char *event =NULL , *group =UPROBE_EVENT_SYSTEM ;char *arg, *filename, *rctr, *rctr_end, *tmp;char buf[MAX_EVENT_NAME_LEN ]; +enum probe_print_type ptype;struct path path;unsigned long offset, ref_ctr_offset;bool is_return = false;@@ -687,7 +688,8 @@ static int __trace_uprobe_create(int argc, const char **argv) goto error; } - ret =traceprobe_set_print_fmt (&tu->tp,is_ret_probe (tu)); + ptype =is_ret_probe (tu) ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; + ret =traceprobe_set_print_fmt (&tu->tp, ptype);if (ret <0 ) goto error;@@ -1578,6 +1580,7 @@ struct trace_event_call * create_local_trace_uprobe(char *name, unsigned long offs, unsigned long ref_ctr_offset, bool is_return) { +enum probe_print_type ptype;struct trace_uprobe *tu;struct path path;int ret;@@ -1612,7 +1615,8 @@ create_local_trace_uprobe(char *name, unsigned long offs, tu->filename =kstrdup (name,GFP_KERNEL );init_trace_event_call (tu); -if (traceprobe_set_print_fmt (&tu->tp,is_ret_probe (tu)) <0 ) { + ptype =is_ret_probe (tu) ?PROBE_PRINT_RETURN :PROBE_PRINT_NORMAL ; +if (traceprobe_set_print_fmt (&tu->tp, ptype) <0 ) { ret = -ENOMEM ; goto error; }Signature · 1 line
2.30.2
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerPATCH v7 7/10 tracing/probes: Have process_fetch_insn() take a void * instead of pt_regs
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
In preparation to allow event probes to use the process_fetch_insn() callback in trace_probe_tmpl.h, change the data passed to it from a pointer to pt_regs, as the event probe will not be using regs, and make it a void pointer instead.
Update the process_fetch_insn() callers for kprobe and uprobe events to have the regs defined in the function and just typecast the void pointer parameter.
--- kernel/trace/trace_kprobe.c | 3 ++- kernel/trace/trace_probe_tmpl.h | 6 +++--- kernel/trace/trace_uprobe.c | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-)
kernel/trace/trace_kprobe.c +2 -1
@@ -1325,9 +1325,10 @@ probe_mem_read(void *dest, void *src, size_t size)/* 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, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base) { +struct pt_regs *regs = rec;unsigned long val; retry:
kernel/trace/trace_probe_tmpl.h +3 -3
@@ -54,7 +54,7 @@ fetch_apply_bitfield(struct fetch_insn *code, void *buf)* If dest is NULL, don't store result and return required dynamic data size. */ static int -process_fetch_insn (struct fetch_insn *code,struct pt_regs *regs, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base); static nokprobe_inline int fetch_store_strlen(unsigned long addr);static nokprobe_inline int
@@ -188,7 +188,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)/* Store the value of each argument */ static nokprobe_inline void -store_trace_args (void *data,struct trace_probe *tp,struct pt_regs *regs, +store_trace_args (void *data,struct trace_probe *tp,void *rec,int header_size,int maxlen) {struct probe_arg *arg;
@@ -203,7 +203,7 @@ store_trace_args(void *data, struct trace_probe *tp, struct pt_regs *regs,/* Point the dynamic data area if needed */ if (unlikely (arg->dynamic)) *dl =make_data_loc (maxlen, dyndata - base); - ret =process_fetch_insn (arg->code, regs, dl, base); + ret =process_fetch_insn (arg->code, rec, dl, base);if (unlikely (ret <0 && arg->dynamic)) { *dl =make_data_loc (0 , dyndata - base); }else {
kernel/trace/trace_uprobe.c +2 -1
@@ -213,9 +213,10 @@ static unsigned long translate_user_vaddr(unsigned long file_offset)/* 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, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base) { +struct pt_regs *regs = rec;unsigned long val;/* 1st stage: get value from context */
Signature · 1 line
2.30.2
Re: [PATCH v7 07/10] tracing/probes: Have process_fetch_insn() take a void * instead of pt_regs
Quoted 6 lines · On Thu, 19 Aug 2021 00:13:28 -0400 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
In preparation to allow event probes to use the
process_fetch_insn()callback intrace_probe_tmpl.h, change the data passed to it from a pointer topt_regs, as the event probe will not be using regs, and make it a void pointer instead.
Yes, it is reasonable to be updated in the separated patch.
Thank you!
Quoted 77 lines
Update the
process_fetch_insn()callers for kprobe and uprobe events to have the regs defined in the function and just typecast the void pointer parameter.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/trace/trace_kprobe.c | 3 ++- kernel/trace/trace_probe_tmpl.h | 6 +++--- kernel/trace/trace_uprobe.c | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-)kernel/trace/trace_kprobe.c +2
-1@@ -1325,9 +1325,10 @@ probe_mem_read(void *dest, void *src, size_t size)/* 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, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base) { +struct pt_regs *regs = rec;unsigned long val; retry:kernel/trace/trace_probe_tmpl.h +3
-3@@ -54,7 +54,7 @@ fetch_apply_bitfield(struct fetch_insn *code, void *buf)* If dest is NULL, don't store result and return required dynamic data size. */ static int -process_fetch_insn (struct fetch_insn *code,struct pt_regs *regs, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base); static nokprobe_inline int fetch_store_strlen(unsigned long addr);static nokprobe_inline int@@ -188,7 +188,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)/* Store the value of each argument */ static nokprobe_inline void -store_trace_args (void *data,struct trace_probe *tp,struct pt_regs *regs, +store_trace_args (void *data,struct trace_probe *tp,void *rec,int header_size,int maxlen) {struct probe_arg *arg;@@ -203,7 +203,7 @@ store_trace_args(void *data, struct trace_probe *tp, struct pt_regs *regs,/* Point the dynamic data area if needed */ if (unlikely (arg->dynamic)) *dl =make_data_loc (maxlen, dyndata - base); - ret =process_fetch_insn (arg->code, regs, dl, base); + ret =process_fetch_insn (arg->code, rec, dl, base);if (unlikely (ret <0 && arg->dynamic)) { *dl =make_data_loc (0 , dyndata - base); }else {kernel/trace/trace_uprobe.c +2
-1@@ -213,9 +213,10 @@ static unsigned long translate_user_vaddr(unsigned long file_offset)/* 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, +process_fetch_insn (struct fetch_insn *code,void *rec,void *dest,void *base) { +struct pt_regs *regs = rec;unsigned long val;/* 1st stage: get value from context */ Signature · 1 line
2.30.2
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerPATCH v7 8/10 tracing: Add a probe that attaches to trace events
In reply to Steven RostedtFrom: "Tzvetomir Stoyanov (VMware)" <[email protected]>
A new dynamic event is introduced: event probe. The event is attached to an existing tracepoint and uses its fields as arguments. The user can specify custom format string of the new event, select what tracepoint arguments will be printed and how to print them.
An event probe is created by writing configuration string in 'dynamic_events' ftrace file:
e[:[SNAME/]ENAME] SYSTEM/EVENT [FETCHARGS] - Set an event probe
-:SNAME/ENAME - Delete an event probe
Where:
SNAME - System name, if omitted 'eprobes' is used.
ENAME - Name of the new event in SNAME, if omitted the SYSTEM_EVENT is used.
SYSTEM - Name of the system, where the tracepoint is defined, mandatory.
EVENT - Name of the tracepoint event in SYSTEM, mandatory.
FETCHARGS - Arguments:
<name>=$<field>[:TYPE] - Fetch given filed of the tracepoint and print
it as given TYPE with given name. Supported
types are:
(u8/u16/u32/u64/s8/s16/s32/s64), basic type
(x8/x16/x32/x64), hexadecimal types
"string", "ustring" and bitfield.Example, attach an event probe on openat system call and print name of the file that will be opened:
echo "e:esys/eopen syscalls/sys_enter_openat file=\$filename:string" >> dynamic_events
A new dynamic event is created in events/esys/eopen/ directory. It
can be deleted with:
echo "-:esys/eopen" >> dynamic_events
Filters, triggers and histograms can be attached to the new event, it can be matched in synthetic events. There is one limitation - an event probe can not be attached to kprobe, uprobe or another event probe.
--- include/linux/trace_events.h | 4 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 5 +- kernel/trace/trace.h | 18 + kernel/trace/trace_eprobe.c | 932 ++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 14 +- kernel/trace/trace_kprobe.c | 8 - kernel/trace/trace_probe.c | 16 +- kernel/trace/trace_probe.h | 6 +- 9 files changed, 991 insertions(+), 13 deletions(-) create mode 100644 kernel/trace/trace_eprobe.c
include/linux/trace_events.h +4 -0
@@ -313,6 +313,7 @@ enum {TRACE_EVENT_FL_DYNAMIC_BIT ,TRACE_EVENT_FL_KPROBE_BIT ,TRACE_EVENT_FL_UPROBE_BIT , + TRACE_EVENT_FL_EPROBE_BIT, };/*
@@ -325,6 +326,7 @@ enum {* DYNAMIC - Event is a dynamic event (created at run time) * KPROBE - Event is a kprobe * UPROBE - Event is a uprobe +* EPROBE - Event is an event probe */ enum {TRACE_EVENT_FL_FILTERED = (1 <<TRACE_EVENT_FL_FILTERED_BIT ),
@@ -335,6 +337,7 @@ enum {TRACE_EVENT_FL_DYNAMIC = (1 <<TRACE_EVENT_FL_DYNAMIC_BIT ),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), };#define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE)
@@ -680,6 +683,7 @@ enum event_trigger_type {ETT_EVENT_ENABLE = (1 <<3 ),ETT_EVENT_HIST = (1 <<4 ),ETT_HIST_ENABLE = (1 <<5 ), + ETT_EVENT_EPROBE = (1 << 6), };extern int filter_match_preds (struct event_filter *filter,void *rec);
kernel/trace/Makefile +1 -0
@@ -77,6 +77,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o endif obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o +obj-$(CONFIG_PROBE_EVENTS) += trace_eprobe.o obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.o obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o
kernel/trace/trace.c +4 -1
@@ -5543,6 +5543,7 @@ static const char readme_msg[] =#ifdef CONFIG_HIST_TRIGGERS "\t s:[synthetic/]<event> <field> [<field>]\n" #endif +"\t e[:[<group>/]<event>] <attached-group>.<attached-event> [<args>]\n" "\t -:[<group>/]<event>\n" #ifdef CONFIG_KPROBE_EVENTS"\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n"
@@ -5552,7 +5553,7 @@ static const char readme_msg[] =" place (uprobe): <path>:<offset>[%return][(ref_ctr_offset)]\n" #endif"\t args: <name>=fetcharg[:type]\n" -"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n" +"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n" #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n" #else
@@ -5567,6 +5568,8 @@ static const char readme_msg[] ="\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" "\t [unsigned] char/int/long\n"#endif +"\t efield: For event probes ('e' types), the field is on of the fields\n" + "\t of the <attached-group>/<attached-event>.\n"#endif " events/\t\t- Directory containing all trace event subsystems:\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
kernel/trace/trace.h +18 -0
@@ -126,6 +126,11 @@ struct kprobe_trace_entry_head {unsigned long ip; }; +struct eprobe_trace_entry_head { +struct trace_entry ent; +unsigned int type; +}; +struct kretprobe_trace_entry_head {struct trace_entry ent;unsigned long func;
@@ -1508,9 +1513,14 @@ static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; }extern int register_trigger_cmds (void );extern void clear_event_triggers (struct trace_array *tr); +enum { +EVENT_TRIGGER_FL_PROBE =BIT (0 ), +}; +struct event_trigger_data {unsigned long count;int ref; +int flags;struct event_trigger_ops *ops;struct event_command *cmd_ops;struct event_filter __rcu *filter;
@@ -1918,6 +1928,14 @@ static inline bool is_good_name(const char *name)return true; } +/* Convert certain expected symbols into '_' when generating event names */ +static inline void sanitize_event_name (char *name) +{ +while (*name++ !='\0' ) +if (*name ==':' || *name =='.' ) + *name ='_' ; +} +/* * This is a generic way to read and write a u64 value from a file in tracefs. *
kernel/trace/trace_eprobe.c new file +932 -0
932 lines not on this page · raw message
kernel/trace/trace_events_trigger.c +13 -1
@@ -124,6 +124,18 @@ static void *trigger_next(struct seq_file *m, void *t, loff_t *pos)return seq_list_next (t, &event_file->triggers, pos); } +static bool check_user_trigger (struct trace_event_file *file) +{ +struct event_trigger_data *data; + +list_for_each_entry_rcu (data, &file->triggers, list) { +if (data->flags &EVENT_TRIGGER_FL_PROBE ) +continue ; +return true; + } +return false; +} +static void *trigger_start (struct seq_file *m,loff_t *pos) {struct trace_event_file *event_file;
@@ -134,7 +146,7 @@ static void *trigger_start(struct seq_file *m, loff_t *pos)if (unlikely (!event_file))return ERR_PTR (-ENODEV ); -if (list_empty (&event_file->triggers)) +if (list_empty (&event_file->triggers) || !check_user_trigger (event_file))return *pos ==0 ?SHOW_AVAILABLE_TRIGGERS :NULL ;return seq_list_start (&event_file->triggers, *pos);
kernel/trace/trace_kprobe.c +0 -8
@@ -703,14 +703,6 @@ static struct notifier_block trace_kprobe_module_nb = { .priority = 1 /* Invoked after kprobe module callback */ }; -/* Convert certain expected symbols into '_' when generating event names */ -static inline void sanitize_event_name (char *name) -{ -while (*name++ !='\0' ) -if (*name ==':' || *name =='.' ) - *name ='_' ; -} -static int __trace_kprobe_create (int argc,const char *argv[]) {/*
kernel/trace/trace_probe.c +14 -2
@@ -319,6 +319,13 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, code->op =FETCH_OP_ARG ; code->param = (unsigned int )param -1 ;#endif + }else if (flags &TPARG_FL_TPOINT ) { +if (code->data) +return -EFAULT ; + code->data =kstrdup (arg,GFP_KERNEL ); +if (!code->data) +return -ENOMEM ; + code->op =FETCH_OP_TP_ARG ; }else goto inval_var;
@@ -646,13 +653,14 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, !strcmp(parg->type->name, "ustring")) {if (code->op !=FETCH_OP_DEREF && code->op !=FETCH_OP_UDEREF && code->op !=FETCH_OP_IMM && code->op !=FETCH_OP_COMM && - code->op !=FETCH_OP_DATA ) { + code->op !=FETCH_OP_DATA && code->op !=FETCH_OP_TP_ARG ) {trace_probe_log_err (offset + (t ? (t - arg) :0 ),BAD_STRING ); goto fail; }if ((code->op ==FETCH_OP_IMM || code->op ==FETCH_OP_COMM || - code->op ==FETCH_OP_DATA ) || parg->count) { + code->op ==FETCH_OP_DATA ) || code->op ==FETCH_OP_TP_ARG || + parg->count) {/* * IMM, DATA and COMM is pointing actual address, those * must be kept, and if parg->count != 0, this is an
@@ -867,6 +875,10 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, fmt ="(%lx <- %lx)" ; arg ="REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP ;break ; +case PROBE_PRINT_EVENT : + fmt ="(%u)" ; + arg ="REC->" FIELD_STRING_TYPE ; +break ;default :WARN_ON_ONCE (1 );return 0 ;
kernel/trace/trace_probe.h +5 -1
@@ -38,6 +38,7 @@#define FIELD_STRING_IP "__probe_ip"#define FIELD_STRING_RETIP "__probe_ret_ip"#define FIELD_STRING_FUNC "__probe_func" +#define FIELD_STRING_TYPE "__probe_type"#undef DEFINE_FIELD#define DEFINE_FIELD (type, item, name, is_signed) \
@@ -102,6 +103,7 @@ enum fetch_op {FETCH_OP_MOD_BF ,/* Bitfield: .basesize, .lshift, .rshift */ // Stage 5 (loop) op FETCH_OP_LP_ARRAY ,/* Array: .param = loop count */ +FETCH_OP_TP_ARG ,/* Trace Point argument */ FETCH_OP_END , FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */ };
@@ -351,7 +353,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char#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) +#define TPARG_FL_TPOINT BIT(3) +#define TPARG_FL_MASK GENMASK(3, 0)extern int traceprobe_parse_probe_arg (struct trace_probe *tp,int i,const char *argv,unsigned int flags);
@@ -366,6 +369,7 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup,enum probe_print_type {PROBE_PRINT_NORMAL ,PROBE_PRINT_RETURN , +PROBE_PRINT_EVENT , };extern int traceprobe_set_print_fmt (struct trace_probe *tp,enum probe_print_type ptype);
Signature · 1 line
2.30.2
Masami Hiramatsu
maintainerRe: [PATCH v7 08/10] tracing: Add a probe that attaches to trace events
Hi Steve,
Thanks for updating.
kernel/trace/trace_eprobe.c line 811
Quoted 12 lines · On Thu, 19 Aug 2021 00:13:29 -0400 Steven Rostedt <[email protected]> wrote:
kernel/trace/trace_eprobe.c new file +12
-0+static bool find_event_probe (const char *group,const char *event) +{ +struct trace_eprobe *ep; +struct dyn_event *ev; +bool ret = false; + +/* +* Must grab the event_mutex to prevent the list from being modified +* by other probes. But the event_probe being only created via the +* dynamic_events file, is only added under the dyn_event_ops_mutex, +* which is currently held. There is no race between this check and +* adding the new probe.
This is not correct, as I said in the previous mail. The dynamic event has 2 lists, one is for the "kind of" dynamic event (dyn_event_ops), and the other one is for the dynamic events itself. The "dyn_event_ops_mutex" is protecting only "dyn_event_ops", and the dynamic event list is ptotected by the "event_mutex". (This is described in the trace_dynevent.c) So holding event_mutex is correct.
Quoted 47 lines
kernel/trace/trace_eprobe.c new file +47
-0+*/ +mutex_lock (&event_mutex); +for_each_dyn_event (ev) { +if (ev->ops != &eprobe_dyn_event_ops) +continue ; + ep =to_trace_eprobe (ev); +if (strcmp (ep->tp.event->class.system, group) ==0 && +strcmp (ep->tp.event->call.name, event) ==0 ) { + ret = true; +break ; + } + } +mutex_lock (&event_mutex); + +return ret; +} + +static int __trace_eprobe_create (int argc,const char *argv[]) +{ +/* +* Argument syntax: +* e[:[GRP/]ENAME] SYSTEM.EVENT [FETCHARGS] +* Fetch args: +* <name>=$<field>[:TYPE] +*/ +const char *event =NULL , *group =EPROBE_EVENT_SYSTEM ; +const char *sys_event =NULL , *sys_name =NULL ; +struct trace_event_call *event_call; +struct trace_eprobe *ep =NULL ; +char buf1[MAX_EVENT_NAME_LEN ]; +char buf2[MAX_EVENT_NAME_LEN ]; +int ret =0 ; +int i; + +if (argc <2 || argv[0 ][0 ] !='e' ) +return -ECANCELED ; + +trace_probe_log_init ("event_probe" , argc, argv); + + event =strchr (&argv[0 ][1 ],':' ); +if (event) { + event++; + ret =traceprobe_parse_event_name (&event, &group, buf1, + event - argv[0 ]); +if (ret) + goto parse_error; + }else {
kernel/trace/trace_eprobe.c line 868
Quoted 10 lines
kernel/trace/trace_eprobe.c new file +10
-0+strscpy (buf1, argv[1 ],MAX_EVENT_NAME_LEN ); +sanitize_event_name (buf1); + event = buf1; + } +if (!is_good_name (event) || !is_good_name (group)) + goto parse_error; + +/* Check if the name already exists */ +if (find_event_probe (group, event)) +return -EEXIST ;
Hmm, there is a window between checking the name confliction here, ...
Quoted 29 lines
kernel/trace/trace_eprobe.c new file +29
-0+ + sys_event = argv[1 ]; + ret =traceprobe_parse_event_name (&sys_event, &sys_name, buf2, + sys_event - argv[1 ]); +if (ret || !sys_name) + goto parse_error; +if (!is_good_name (sys_event) || !is_good_name (sys_name)) + goto parse_error; + +mutex_lock (&event_mutex); + event_call =find_and_get_event (sys_name, sys_event); + ep =alloc_event_probe (group, event, event_call, argc -2 ); +mutex_unlock (&event_mutex); + +if (IS_ERR (ep)) { + ret =PTR_ERR (ep); +/* This must return -ENOMEM, else there is a bug */ +WARN_ON_ONCE (ret != -ENOMEM ); + goto error;/* We know ep is not allocated */ + } + + argc -=2 ; argv +=2 ; +/* parse arguments */ +for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { +trace_probe_log_set_index (i +2 ); + ret =trace_eprobe_tp_update_arg (ep, argv, i); +if (ret) + goto error; + }
kernel/trace/trace_eprobe.c line 907
Quoted 10 lines
kernel/trace/trace_eprobe.c new file +10
-0+ ret =traceprobe_set_print_fmt (&ep->tp,PROBE_PRINT_EVENT ); +if (ret <0 ) + goto error; +init_trace_eprobe_call (ep); +mutex_lock (&event_mutex); + ret =trace_probe_register_event_call (&ep->tp); +if (ret) { +mutex_unlock (&event_mutex); + goto error; + }
... and register it here.
Between the existance check and the registration, someone can register same name event probe. So I recommend you to do it as;
static int register_event_probe(ep)
{
init_trace_eprobe_call(ep);
mutex_lock(&event_mutex);
if (find_event_probe(group, event))
ret = -EEXIST;
goto out;
}
ret = trace_probe_register_event_call(&ep->tp);
if (ret)
goto out;
ret = dyn_event_add(&ep->devent, &ep->tp.event->call);
mutex_unlock(&event_mutex);
out:
return ret;
}Anyway, I will send a patch for fixing related issue. If you don't care the name collision between eprobes or other events, you can just apply it. Then trace_probe_register_event_call() will reject the same name event.
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Masami Hiramatsu
maintainerPATCH 1/1 tracing/probes: Reject events which have the same name of existing one
Since kprobe_events and uprobe_events only check whether the other same-type probe event has the same name or not, if the user gives the same name of the existing tracepoint event (or the other type of probe events), it silently fails to create the tracefs entry (but registered.) as below.
/sys/kernel/tracing # ls events/task/task_rename
enable filter format hist id trigger/sys/kernel/tracing # echo p:task/task_rename vfs_read >> kprobe_events
[ 113.048508] Could not create tracefs 'task_rename' directory
/sys/kernel/tracing # cat kprobe_events
p:task/task_rename vfs_read
To fix this issue, check whether the existing events have the same name or not in trace_probe_register_event_call(). If exists, it rejects to register the new event.
---
Steve, I think this is also good to your eprobe series.
--- kernel/trace/trace_kprobe.c | 6 +++++- kernel/trace/trace_probe.c | 25 +++++++++++++++++++++++++ kernel/trace/trace_probe.h | 1 + kernel/trace/trace_uprobe.c | 6 +++++- 4 files changed, 36 insertions(+), 2 deletions(-)
kernel/trace/trace_kprobe.c +5 -1
@@ -647,7 +647,11 @@ static int register_trace_kprobe(struct trace_kprobe *tk)/* Register new event */ ret =register_kprobe_event (tk);if (ret) { -pr_warn ("Failed to register probe event(%d)\n" , ret); +if (ret == -EEXIST ) { +trace_probe_log_set_index (0 ); +trace_probe_log_err (0 ,EVENT_EXIST ); + }else +pr_warn ("Failed to register probe event(%d)\n" , ret); goto end; }
kernel/trace/trace_probe.c +25 -0
@@ -1029,11 +1029,36 @@ int trace_probe_init(struct trace_probe *tp, const char *event,return ret; } +static struct trace_event_call * +find_trace_event_call (const char *system,const char *event_name) +{ +struct trace_event_call *tp_event; +const char *name; + +list_for_each_entry (tp_event, &ftrace_events, list) { +if (!tp_event->class->system || +strcmp (system, tp_event->class->system)) +continue ; + name =trace_event_name (tp_event); +if (!name ||strcmp (event_name, name)) +continue ; +return tp_event; + } + +return NULL ; +} +int trace_probe_register_event_call (struct trace_probe *tp) {struct trace_event_call *call =trace_probe_event_call (tp);int ret; +lockdep_assert_held (&event_mutex); + +if (find_trace_event_call (trace_probe_group_name (tp), +trace_probe_name (tp))) +return -EEXIST ; + ret =register_trace_event (&call->event);if (!ret)return -ENODEV ;
kernel/trace/trace_probe.h +1 -0
@@ -399,6 +399,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,C (NO_EVENT_NAME ,"Event name is not specified" ), \C (EVENT_TOO_LONG ,"Event name is too long" ), \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 (RETVAL_ON_PROBE ,"$retval is not available on probe" ), \C (BAD_STACK_NUM ,"Invalid stack number" ), \ C(BAD_ARG_NUM, "Invalid argument number"), \
kernel/trace/trace_uprobe.c +5 -1
@@ -514,7 +514,11 @@ static int register_trace_uprobe(struct trace_uprobe *tu) ret =register_uprobe_event (tu);if (ret) { -pr_warn ("Failed to register probe event(%d)\n" , ret); +if (ret == -EEXIST ) { +trace_probe_log_set_index (0 ); +trace_probe_log_err (0 ,EVENT_EXIST ); + }else +pr_warn ("Failed to register probe event(%d)\n" , ret); goto end; }
Steven Rostedt
maintainerRe: [PATCH v7 08/10] tracing: Add a probe that attaches to trace events
In reply to Masami HiramatsuQuoted 7 lines · On Thu, 19 Aug 2021 19:22:58 +0900 Masami Hiramatsu <[email protected]> wrote:
Hi Steve,
Thanks for updating.
On Thu, 19 Aug 2021 00:13:29 -0400
Steven Rostedt <[email protected]> wrote:
Quoted 14 lines
kernel/trace/trace_eprobe.c line 811
Quoted 12 lines
kernel/trace/trace_eprobe.c new file +12
-0+static bool find_event_probe (const char *group,const char *event) +{ +struct trace_eprobe *ep; +struct dyn_event *ev; +bool ret = false; + +/* +* Must grab the event_mutex to prevent the list from being modified +* by other probes. But the event_probe being only created via the +* dynamic_events file, is only added under the dyn_event_ops_mutex, +* which is currently held. There is no race between this check and +* adding the new probe. This is not correct, as I said in the previous mail. The dynamic event has
Actually the above is correct, because it states that it wont race with another event probe. You are correct in that it does not protect against racing against other events. And if you look at the check below, it only looks at eprobes:
if (ev->ops != &eprobe_dyn_event_ops) continue;
Quoted 57 lines
2 lists, one is for the "kind of" dynamic event (
dyn_event_ops), and the other one is for the dynamic events itself. The "dyn_event_ops_mutex" is protecting only "dyn_event_ops", and the dynamic event list is ptotected by the "event_mutex". (This is described in thetrace_dynevent.c) So holdingevent_mutexis correct.Quoted 51 lines
kernel/trace/trace_eprobe.c new file +51
-0+*/ +mutex_lock (&event_mutex); +for_each_dyn_event (ev) { +if (ev->ops != &eprobe_dyn_event_ops) +continue ; + ep =to_trace_eprobe (ev); +if (strcmp (ep->tp.event->class.system, group) ==0 && +strcmp (ep->tp.event->call.name, event) ==0 ) { + ret = true; +break ; + } + } +mutex_lock (&event_mutex); + +return ret; +} + +static int __trace_eprobe_create (int argc,const char *argv[]) +{ +/* +* Argument syntax: +* e[:[GRP/]ENAME] SYSTEM.EVENT [FETCHARGS] +* Fetch args: +* <name>=$<field>[:TYPE] +*/ +const char *event =NULL , *group =EPROBE_EVENT_SYSTEM ; +const char *sys_event =NULL , *sys_name =NULL ; +struct trace_event_call *event_call; +struct trace_eprobe *ep =NULL ; +char buf1[MAX_EVENT_NAME_LEN ]; +char buf2[MAX_EVENT_NAME_LEN ]; +int ret =0 ; +int i; + +if (argc <2 || argv[0 ][0 ] !='e' ) +return -ECANCELED ; + +trace_probe_log_init ("event_probe" , argc, argv); + + event =strchr (&argv[0 ][1 ],':' ); +if (event) { + event++; + ret =traceprobe_parse_event_name (&event, &group, buf1, + event - argv[0 ]); +if (ret) + goto parse_error; + }else { +strscpy (buf1, argv[1 ],MAX_EVENT_NAME_LEN ); +sanitize_event_name (buf1); + event = buf1; + }
Quoted 8 lines
kernel/trace/trace_eprobe.c line 868
Quoted 6 lines
kernel/trace/trace_eprobe.c new file +6
-0+if (!is_good_name (event) || !is_good_name (group)) + goto parse_error; + +/* Check if the name already exists */ +if (find_event_probe (group, event)) +return -EEXIST ;Hmm, there is a window between checking the name confliction here, ...
For non eprobes, you are correct.
Quoted 43 lines
Quoted 30 lines
kernel/trace/trace_eprobe.c new file +29
-0+ + sys_event = argv[1 ]; + ret =traceprobe_parse_event_name (&sys_event, &sys_name, buf2, + sys_event - argv[1 ]); +if (ret || !sys_name) + goto parse_error; +if (!is_good_name (sys_event) || !is_good_name (sys_name)) + goto parse_error; + +mutex_lock (&event_mutex); + event_call =find_and_get_event (sys_name, sys_event); + ep =alloc_event_probe (group, event, event_call, argc -2 ); +mutex_unlock (&event_mutex); + +if (IS_ERR (ep)) { + ret =PTR_ERR (ep); +/* This must return -ENOMEM, else there is a bug */ +WARN_ON_ONCE (ret != -ENOMEM ); + goto error;/* We know ep is not allocated */ + } + + argc -=2 ; argv +=2 ; +/* parse arguments */ +for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { +trace_probe_log_set_index (i +2 ); + ret =trace_eprobe_tp_update_arg (ep, argv, i); +if (ret) + goto error; + }kernel/trace/trace_eprobe.c line 907
Quoted 10 lines
kernel/trace/trace_eprobe.c new file +10
-0+ ret =traceprobe_set_print_fmt (&ep->tp,PROBE_PRINT_EVENT ); +if (ret <0 ) + goto error; +init_trace_eprobe_call (ep); +mutex_lock (&event_mutex); + ret =trace_probe_register_event_call (&ep->tp); +if (ret) { +mutex_unlock (&event_mutex); + goto error; + }... and register it here.
Quoted 2 lines
Between the existance check and the registration, someone can register same name event probe. So I recommend you to do it as;
Actually they can't because eprobes are only created by this function, and this is only called by trace_dynevent.c:
static int create_dyn_event(const char *raw_command)
{
struct dyn_event_operations *ops;
int ret = -ENODEV;
if (raw_command[0] == '-' || raw_command[0] == '!')
return dyn_event_release(raw_command, NULL);
mutex_lock(&dyn_event_ops_mutex); <=================== Lock taken here
list_for_each_entry(ops, &dyn_event_ops_list, list) {
ret = ops->create(raw_command); <============= This function called here
if (!ret || ret != -ECANCELED)
break;
}
mutex_unlock(&dyn_event_ops_mutex); <================= Lock released
if (ret == -ECANCELED)
ret = -EINVAL;
return ret;
}But kprobes and uprobes can be created outside this loop.
So my current logic protects against duplicate eprobes, but does not protect against kprobes or uprobes with the same name.
Quoted 19 lines
static int
register_event_probe(ep){ init_trace_eprobe_call(ep); mutex_lock(&event_mutex); if (find_event_probe(group, event)) ret = -EEXIST; goto out; } ret = trace_probe_register_event_call(&ep->tp); if (ret) goto out; ret = dyn_event_add(&ep->devent, &ep->tp.event->call); mutex_unlock(&event_mutex); out: return ret; }
Quoted 3 lines
Anyway, I will send a patch for fixing related issue. If you don't care the name collision between eprobes or other events, you can just apply it. Then
trace_probe_register_event_call()will reject the same name event.
Thanks, I'll take a look at it, and try to incorporate it.
I'll pull in all the patches that you have already acked into my linux-next queue, and then only send out those that are still under review.
-- Steve
Steven Rostedt
maintainerPATCH v7 9/10 selftests/ftrace: Add clear_dynamic_events() to test cases
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Add a function to remove all dynamic events from the tracing directory. It requires a loop as some of the dynamic events may depend on others being removed first. Also add a safety that prevents it from looping infinitely due to a bug where an event never gets removed.
--- .../testing/selftests/ftrace/test.d/functions | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+)
tools/testing/selftests/ftrace/test.d/functions +22 -0
@@ -83,6 +83,27 @@ clear_synthetic_events() { # reset all current synthetic events done } +clear_dynamic_events() { # reset all current dynamic events + again=1 + stop=1 + # loop mulitple times as some events require other to be removed first + while [ $again -eq 1 ]; do + stop=$((stop+1)) + # Prevent infinite loops + if [ $stop -gt 10 ]; then + break; + fi + again=2 + grep -v '^#' dynamic_events| + while read line; do + del=`sed -e 's/^.\([^ ]*\).*/-\1/'` + if ! echo "$del" >> dynamic_events; then + again=1 + fi + done + done +} + initialize_ftrace() { # Reset ftrace to initial-state # As the initial state, ftrace will be set to nop tracer, # no events, no triggers, no filters, no function filters,
@@ -93,6 +114,7 @@ initialize_ftrace() { # Reset ftrace to initial-state reset_events_filter reset_ftrace_filter disable_events + clear_dynamic_events [ -f set_event_pid ] && echo > set_event_pid [ -f set_ftrace_pid ] && echo > set_ftrace_pid [ -f set_ftrace_notrace ] && echo > set_ftrace_notrace
Signature · 1 line
2.30.2
Steven Rostedt
maintainerRe: [PATCH v7 09/10] selftests/ftrace: Add clear_dynamic_events() to test cases
Quoted 4 lines · On Thu, 19 Aug 2021 00:13:30 -0400 Steven Rostedt <[email protected]> wrote:
tools/testing/selftests/ftrace/test.d/functions +4
-0+clear_dynamic_events() { # reset all current dynamic events + again=1 + stop=1 + # loop mulitple times as some events require other to be removed first
tools/testing/selftests/ftrace/test.d/functions clear_synthetic_events() { # reset all current synthetic events · line 99
Quoted 10 lines
tools/testing/selftests/ftrace/test.d/functions +10
-0+ while [ $again -eq 1 ]; do + stop=$((stop+1)) + # Prevent infinite loops + if [ $stop -gt 10 ]; then + break; + fi + again=2 + grep -v '^#' dynamic_events| + while read line; do + del=`sed -e 's/^.\([^ ]*\).*/-\1/'`
That should be "del=echo $line | sed -e 's/^.\([^ ]*\).*/-\1/'
Will fix in v8.
-- Steve
Quoted 6 lines
tools/testing/selftests/ftrace/test.d/functions +6
-0+ if ! echo "$del" >> dynamic_events; then + again=1 + fi + done + done +}
Steven Rostedt
maintainerPATCH v7 10/10 selftests/ftrace: Add selftest for testing eprobe events
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Add a test to test event probes, by creating a synthetic event across sys_enter_openat and sys_exit_openat that passes the filename pointer from the enter of the system call to the exit, and then add an event probe to the synthetic event to make sure that the file name is seen.
--- .../test.d/dynevent/add_remove_eprobe.tc | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc new file +53 -0
@@ -0,0 +1,53 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove eprobe events +# requires: dynamic_events "e[:[<group>/]<event>] <attached-group>.<attached-event> [<args>]":README + +echo 0 > events/enable + +clear_dynamic_events + +SYSTEM="syscalls" +START="sys_enter_openat" +END="sys_exit_openat" +FIELD="filename" +SYNTH="synth_open" +EPROBE="eprobe_open" + +echo "$SYNTH u64 filename; s64 ret;" > synthetic_events +echo "hist:keys=common_pid:__arg__1=$FIELD" > events/$SYSTEM/$START/trigger +echo "hist:keys=common_pid:filename=\$__arg__1,ret=ret:onmatch($SYSTEM.$START).trace($SYNTH,\$filename,\$ret)" > events/$SYSTEM/$END/trigger + +echo "e:$EPROBE synthetic/$SYNTH file=+0(\$filename):ustring ret=\$ret:s64" >> dynamic_events + +grep -q "$SYNTH" dynamic_events +grep -q "$EPROBE" dynamic_events +test -d events/synthetic/$SYNTH +test -d events/eprobes/$EPROBE + +echo 1 > events/eprobes/$EPROBE/enable +ls +echo 0 > events/eprobes/$EPROBE/enable + +content=`grep '^ *ls-' trace | grep 'file='` +nocontent=`grep '^ *ls-' trace | grep 'file=' | grep -v -e '"/' -e '"."'` || true + +if [ -z "$content" ]; then + exit_fail +fi + +if [ ! -z "$nocontent" ]; then + exit_fail +fi + +echo "-:$EPROBE" >> dynamic_events +echo '!'"hist:keys=common_pid:filename=\$__arg__1,ret=ret:onmatch($SYSTEM.$START).trace($SYNTH,\$filename,\$ret)" > events/$SYSTEM/$END/trigger +echo '!'"hist:keys=common_pid:__arg__1=$FIELD" > events/$SYSTEM/$START/trigger +echo '!'"$SYNTH u64 filename; s64 ret;" >> synthetic_events + +! grep -q "$SYNTH" dynamic_events +! grep -q "$EPROBE" dynamic_events +! test -d events/synthetic/$SYNTH +! test -d events/eprobes/$EPROBE + +clear_trace
Signature · 1 line
2.30.2