linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 0/5] tracing: Creation of event probe
@ 2021-08-19 15:26 Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 1/5] tracing/probes: Reject events which have the same name of existing one Steven Rostedt
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi

V7 Link: https://lore.kernel.org/linux-trace-devel/20210819041321.105110033@goodmis.org/

Changes from v7:

I moved the patches that have been acked by Masami into my linux-next queue
and removed them from this patch series.

I added Masami's patch to check for duplicate events and return a proper
error message about the duplication.

I changed the duplication check in the eprobe code to match Masami's
changes.

I fixed a bug in the removing of dynamic events in the selftest code.

Added a new test to make sure the duplicate events are caught.


Masami Hiramatsu (1):
      tracing/probes: Reject events which have the same name of existing one

Steven Rostedt (VMware) (3):
      selftests/ftrace: Add clear_dynamic_events() to test cases
      selftests/ftrace: Add selftest for testing eprobe events
      selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes

Tzvetomir Stoyanov (VMware) (1):
      tracing: Add a probe that attaches to trace events

----
 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                        | 903 +++++++++++++++++++++
 kernel/trace/trace_events_trigger.c                |  14 +-
 kernel/trace/trace_kprobe.c                        |  14 +-
 kernel/trace/trace_probe.c                         |  41 +-
 kernel/trace/trace_probe.h                         |   7 +-
 kernel/trace/trace_uprobe.c                        |   6 +-
 .../ftrace/test.d/dynevent/add_remove_eprobe.tc    |  53 ++
 .../ftrace/test.d/dynevent/test_duplicates.tc      |  28 +
 tools/testing/selftests/ftrace/test.d/functions    |  22 +
 13 files changed, 1101 insertions(+), 15 deletions(-)
 create mode 100644 kernel/trace/trace_eprobe.c
 create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
 create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v8 1/5] tracing/probes: Reject events which have the same name of existing one
  2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
@ 2021-08-19 15:26 ` Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 2/5] tracing: Add a probe that attaches to trace events Steven Rostedt
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi

From: Masami Hiramatsu <mhiramat@kernel.org>

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.

Link: https://lkml.kernel.org/r/162936876189.187130.17558311387542061930.stgit@devnote2

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 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(-)

diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 4b013d24f5a9..882c27044029 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -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;
 	}
 
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 9c9c83a063b2..782c00eb6859 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1047,11 +1047,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;
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 8adf5f3542a6..66701a92d186 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -404,6 +404,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"),		\
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index d219ba50efbd..225ce569bf8f 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -515,7 +515,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;
 	}
 
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v8 2/5] tracing: Add a probe that attaches to trace events
  2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 1/5] tracing/probes: Reject events which have the same name of existing one Steven Rostedt
@ 2021-08-19 15:26 ` Steven Rostedt
  2021-08-20  0:10   ` Masami Hiramatsu
  2021-08-19 15:26 ` [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases Steven Rostedt
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi

From: "Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com>

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.

Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
Link: https://lkml.kernel.org/r/20210819041842.485382601@goodmis.org

Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 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         | 903 ++++++++++++++++++++++++++++
 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, 962 insertions(+), 13 deletions(-)
 create mode 100644 kernel/trace/trace_eprobe.c

diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 9564c4d9a3b6..0a0144580bbd 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -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);
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index b1c47ccf4f73..6de5d4d63165 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -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
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8425c3d70895..489924cde4f8 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -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"
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 4a0e693000c6..b7c0f8e160fb 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -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.
  *
diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c
new file mode 100644
index 000000000000..56a96e9750cf
--- /dev/null
+++ b/kernel/trace/trace_eprobe.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * event probes
+ *
+ * Part of this code was copied from kernel/trace/trace_kprobe.c written by
+ * Masami Hiramatsu <mhiramat@kernel.org>
+ *
+ * Copyright (C) 2021, VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ * Copyright (C) 2021, VMware Inc, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/ftrace.h>
+
+#include "trace_dynevent.h"
+#include "trace_probe.h"
+#include "trace_probe_tmpl.h"
+
+#define EPROBE_EVENT_SYSTEM "eprobes"
+
+struct trace_eprobe {
+	/* tracepoint system */
+	const char *event_system;
+
+	/* tracepoint event */
+	const char *event_name;
+
+	struct trace_event_call *event;
+
+	struct dyn_event	devent;
+	struct trace_probe	tp;
+};
+
+struct eprobe_data {
+	struct trace_event_file	*file;
+	struct trace_eprobe	*ep;
+};
+
+static int __trace_eprobe_create(int argc, const char *argv[]);
+
+static void trace_event_probe_cleanup(struct trace_eprobe *ep)
+{
+	if (!ep)
+		return;
+	trace_probe_cleanup(&ep->tp);
+	kfree(ep->event_name);
+	kfree(ep->event_system);
+	if (ep->event)
+		trace_event_put_ref(ep->event);
+	kfree(ep);
+}
+
+static struct trace_eprobe *to_trace_eprobe(struct dyn_event *ev)
+{
+	return container_of(ev, struct trace_eprobe, devent);
+}
+
+static int eprobe_dyn_event_create(const char *raw_command)
+{
+	return trace_probe_create(raw_command, __trace_eprobe_create);
+}
+
+static int eprobe_dyn_event_show(struct seq_file *m, struct dyn_event *ev)
+{
+	struct trace_eprobe *ep = to_trace_eprobe(ev);
+	int i;
+
+	seq_printf(m, "e:%s/%s", trace_probe_group_name(&ep->tp),
+				trace_probe_name(&ep->tp));
+	seq_printf(m, " %s.%s", ep->event_system, ep->event_name);
+
+	for (i = 0; i < ep->tp.nr_args; i++)
+		seq_printf(m, " %s=%s", ep->tp.args[i].name, ep->tp.args[i].comm);
+	seq_putc(m, '\n');
+
+	return 0;
+}
+
+static int unregister_trace_eprobe(struct trace_eprobe *ep)
+{
+	/* If other probes are on the event, just unregister eprobe */
+	if (trace_probe_has_sibling(&ep->tp))
+		goto unreg;
+
+	/* Enabled event can not be unregistered */
+	if (trace_probe_is_enabled(&ep->tp))
+		return -EBUSY;
+
+	/* Will fail if probe is being used by ftrace or perf */
+	if (trace_probe_unregister_event_call(&ep->tp))
+		return -EBUSY;
+
+unreg:
+	dyn_event_remove(&ep->devent);
+	trace_probe_unlink(&ep->tp);
+
+	return 0;
+}
+
+static int eprobe_dyn_event_release(struct dyn_event *ev)
+{
+	struct trace_eprobe *ep = to_trace_eprobe(ev);
+	int ret = unregister_trace_eprobe(ep);
+
+	if (!ret)
+		trace_event_probe_cleanup(ep);
+	return ret;
+}
+
+static bool eprobe_dyn_event_is_busy(struct dyn_event *ev)
+{
+	struct trace_eprobe *ep = to_trace_eprobe(ev);
+
+	return trace_probe_is_enabled(&ep->tp);
+}
+
+static bool eprobe_dyn_event_match(const char *system, const char *event,
+			int argc, const char **argv, struct dyn_event *ev)
+{
+	struct trace_eprobe *ep = to_trace_eprobe(ev);
+
+	return strcmp(trace_probe_name(&ep->tp), event) == 0 &&
+	    (!system || strcmp(trace_probe_group_name(&ep->tp), system) == 0) &&
+	    trace_probe_match_command_args(&ep->tp, argc, argv);
+}
+
+static struct dyn_event_operations eprobe_dyn_event_ops = {
+	.create = eprobe_dyn_event_create,
+	.show = eprobe_dyn_event_show,
+	.is_busy = eprobe_dyn_event_is_busy,
+	.free = eprobe_dyn_event_release,
+	.match = eprobe_dyn_event_match,
+};
+
+static struct trace_eprobe *alloc_event_probe(const char *group,
+					      const char *this_event,
+					      struct trace_event_call *event,
+					      int nargs)
+{
+	struct trace_eprobe *ep;
+	const char *event_name;
+	const char *sys_name;
+	int ret = -ENOMEM;
+
+	if (!event)
+		return ERR_PTR(-ENODEV);
+
+	sys_name = event->class->system;
+	event_name = trace_event_name(event);
+
+	ep = kzalloc(struct_size(ep, tp.args, nargs), GFP_KERNEL);
+	if (!ep) {
+		trace_event_put_ref(ep->event);
+		goto error;
+	}
+	ep->event = event;
+	ep->event_name = kstrdup(event_name, GFP_KERNEL);
+	if (!ep->event_name)
+		goto error;
+	ep->event_system = kstrdup(sys_name, GFP_KERNEL);
+	if (!ep->event_system)
+		goto error;
+
+	ret = trace_probe_init(&ep->tp, this_event, group, false);
+	if (ret < 0)
+		goto error;
+
+	dyn_event_init(&ep->devent, &eprobe_dyn_event_ops);
+	return ep;
+error:
+	trace_event_probe_cleanup(ep);
+	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;
+
+	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;
+		}
+	}
+	kfree(parg->code->data);
+	parg->code->data = NULL;
+	return -ENOENT;
+}
+
+static int eprobe_event_define_fields(struct trace_event_call *event_call)
+{
+	int ret;
+	struct eprobe_trace_entry_head field;
+	struct trace_probe *tp;
+
+	tp = trace_probe_primary_from_call(event_call);
+	if (WARN_ON_ONCE(!tp))
+		return -ENOENT;
+
+	DEFINE_FIELD(unsigned int, type, FIELD_STRING_TYPE, 0);
+
+	return traceprobe_define_arg_fields(event_call, sizeof(field), tp);
+}
+
+static struct trace_event_fields eprobe_fields_array[] = {
+	{ .type = TRACE_FUNCTION_TYPE,
+	  .define_fields = eprobe_event_define_fields },
+	{}
+};
+
+/* Event entry printers */
+static enum print_line_t
+print_eprobe_event(struct trace_iterator *iter, int flags,
+		   struct trace_event *event)
+{
+	struct eprobe_trace_entry_head *field;
+	struct trace_event_call *pevent;
+	struct trace_event *probed_event;
+	struct trace_seq *s = &iter->seq;
+	struct trace_probe *tp;
+
+	field = (struct eprobe_trace_entry_head *)iter->ent;
+	tp = trace_probe_primary_from_call(
+		container_of(event, struct trace_event_call, event));
+	if (WARN_ON_ONCE(!tp))
+		goto out;
+
+	trace_seq_printf(s, "%s: (", trace_probe_name(tp));
+
+	probed_event = ftrace_find_event(field->type);
+	if (probed_event) {
+		pevent = container_of(probed_event, struct trace_event_call, event);
+		trace_seq_printf(s, "%s.%s", pevent->class->system,
+				 trace_event_name(pevent));
+	} else {
+		trace_seq_printf(s, "%u", field->type);
+	}
+
+	trace_seq_putc(s, ')');
+
+	if (print_probe_args(s, tp->args, tp->nr_args,
+			     (u8 *)&field[1], field) < 0)
+		goto out;
+
+	trace_seq_putc(s, '\n');
+ out:
+	return trace_handle_return(s);
+}
+
+static unsigned long get_event_field(struct fetch_insn *code, void *rec)
+{
+	struct ftrace_event_field *field = code->data;
+	unsigned long val;
+	void *addr;
+
+	addr = rec + field->offset;
+
+	switch (field->size) {
+	case 1:
+		if (field->is_signed)
+			val = *(char *)addr;
+		else
+			val = *(unsigned char *)addr;
+		break;
+	case 2:
+		if (field->is_signed)
+			val = *(short *)addr;
+		else
+			val = *(unsigned short *)addr;
+		break;
+	case 4:
+		if (field->is_signed)
+			val = *(int *)addr;
+		else
+			val = *(unsigned int *)addr;
+		break;
+	default:
+		if (field->is_signed)
+			val = *(long *)addr;
+		else
+			val = *(unsigned long *)addr;
+		break;
+	}
+	return val;
+}
+
+static int get_eprobe_size(struct trace_probe *tp, void *rec)
+{
+	struct probe_arg *arg;
+	int i, len, ret = 0;
+
+	for (i = 0; i < tp->nr_args; i++) {
+		arg = tp->args + i;
+		if (unlikely(arg->dynamic)) {
+			unsigned long val;
+
+			val = get_event_field(arg->code, rec);
+			len = process_fetch_insn_bottom(arg->code + 1, val, NULL, NULL);
+			if (len > 0)
+				ret += len;
+		}
+	}
+
+	return ret;
+}
+
+/* Kprobe specific fetch functions */
+
+/* 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, void *rec, void *dest,
+		   void *base)
+{
+	unsigned long val;
+
+	val = get_event_field(code, rec);
+	return process_fetch_insn_bottom(code + 1, val, dest, base);
+}
+NOKPROBE_SYMBOL(process_fetch_insn)
+
+/* Return the length of string -- including null terminal byte */
+static nokprobe_inline int
+fetch_store_strlen_user(unsigned long addr)
+{
+	const void __user *uaddr =  (__force const void __user *)addr;
+
+	return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
+}
+
+/* Return the length of string -- including null terminal byte */
+static nokprobe_inline int
+fetch_store_strlen(unsigned long addr)
+{
+	int ret, len = 0;
+	u8 c;
+
+#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
+	if (addr < TASK_SIZE)
+		return fetch_store_strlen_user(addr);
+#endif
+
+	do {
+		ret = copy_from_kernel_nofault(&c, (u8 *)addr + len, 1);
+		len++;
+	} while (c && ret == 0 && len < MAX_STRING_SIZE);
+
+	return (ret < 0) ? ret : len;
+}
+
+/*
+ * Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf
+ * with max length and relative data location.
+ */
+static nokprobe_inline int
+fetch_store_string_user(unsigned long addr, void *dest, void *base)
+{
+	const void __user *uaddr =  (__force const void __user *)addr;
+	int maxlen = get_loc_len(*(u32 *)dest);
+	void *__dest;
+	long ret;
+
+	if (unlikely(!maxlen))
+		return -ENOMEM;
+
+	__dest = get_loc_data(dest, base);
+
+	ret = strncpy_from_user_nofault(__dest, uaddr, maxlen);
+	if (ret >= 0)
+		*(u32 *)dest = make_data_loc(ret, __dest - base);
+
+	return ret;
+}
+
+/*
+ * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
+ * length and relative data location.
+ */
+static nokprobe_inline int
+fetch_store_string(unsigned long addr, void *dest, void *base)
+{
+	int maxlen = get_loc_len(*(u32 *)dest);
+	void *__dest;
+	long ret;
+
+#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
+	if ((unsigned long)addr < TASK_SIZE)
+		return fetch_store_string_user(addr, dest, base);
+#endif
+
+	if (unlikely(!maxlen))
+		return -ENOMEM;
+
+	__dest = get_loc_data(dest, base);
+
+	/*
+	 * Try to get string again, since the string can be changed while
+	 * probing.
+	 */
+	ret = strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen);
+	if (ret >= 0)
+		*(u32 *)dest = make_data_loc(ret, __dest - base);
+
+	return ret;
+}
+
+static nokprobe_inline int
+probe_mem_read_user(void *dest, void *src, size_t size)
+{
+	const void __user *uaddr =  (__force const void __user *)src;
+
+	return copy_from_user_nofault(dest, uaddr, size);
+}
+
+static nokprobe_inline int
+probe_mem_read(void *dest, void *src, size_t size)
+{
+#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
+	if ((unsigned long)src < TASK_SIZE)
+		return probe_mem_read_user(dest, src, size);
+#endif
+	return copy_from_kernel_nofault(dest, src, size);
+}
+
+/* eprobe handler */
+static inline void
+__eprobe_trace_func(struct eprobe_data *edata, void *rec)
+{
+	struct eprobe_trace_entry_head *entry;
+	struct trace_event_call *call = trace_probe_event_call(&edata->ep->tp);
+	struct trace_event_buffer fbuffer;
+	int dsize;
+
+	if (WARN_ON_ONCE(call != edata->file->event_call))
+		return;
+
+	if (trace_trigger_soft_disabled(edata->file))
+		return;
+
+	fbuffer.trace_ctx = tracing_gen_ctx();
+	fbuffer.trace_file = edata->file;
+
+	dsize = get_eprobe_size(&edata->ep->tp, rec);
+	fbuffer.regs = NULL;
+
+	fbuffer.event =
+		trace_event_buffer_lock_reserve(&fbuffer.buffer, edata->file,
+					call->event.type,
+					sizeof(*entry) + edata->ep->tp.size + dsize,
+					fbuffer.trace_ctx);
+	if (!fbuffer.event)
+		return;
+
+	entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
+	if (edata->ep->event)
+		entry->type = edata->ep->event->event.type;
+	else
+		entry->type = 0;
+	store_trace_args(&entry[1], &edata->ep->tp, rec, sizeof(*entry), dsize);
+
+	trace_event_buffer_commit(&fbuffer);
+}
+
+/*
+ * The event probe implementation uses event triggers to get access to
+ * the event it is attached to, but is not an actual trigger. The below
+ * functions are just stubs to fulfill what is needed to use the trigger
+ * infrastructure.
+ */
+static int eprobe_trigger_init(struct event_trigger_ops *ops,
+			       struct event_trigger_data *data)
+{
+	return 0;
+}
+
+static void eprobe_trigger_free(struct event_trigger_ops *ops,
+				struct event_trigger_data *data)
+{
+
+}
+
+static int eprobe_trigger_print(struct seq_file *m,
+				struct event_trigger_ops *ops,
+				struct event_trigger_data *data)
+{
+	/* Do not print eprobe event triggers */
+	return 0;
+}
+
+static void eprobe_trigger_func(struct event_trigger_data *data,
+				struct trace_buffer *buffer, void *rec,
+				struct ring_buffer_event *rbe)
+{
+	struct eprobe_data *edata = data->private_data;
+
+	__eprobe_trace_func(edata, rec);
+}
+
+static struct event_trigger_ops eprobe_trigger_ops = {
+	.func			= eprobe_trigger_func,
+	.print			= eprobe_trigger_print,
+	.init			= eprobe_trigger_init,
+	.free			= eprobe_trigger_free,
+};
+
+static int eprobe_trigger_cmd_func(struct event_command *cmd_ops,
+				   struct trace_event_file *file,
+				   char *glob, char *cmd, char *param)
+{
+	return -1;
+}
+
+static int eprobe_trigger_reg_func(char *glob, struct event_trigger_ops *ops,
+				 struct event_trigger_data *data,
+				 struct trace_event_file *file)
+{
+	return -1;
+}
+
+static void eprobe_trigger_unreg_func(char *glob, struct event_trigger_ops *ops,
+				    struct event_trigger_data *data,
+				    struct trace_event_file *file)
+{
+
+}
+
+static struct event_trigger_ops *eprobe_trigger_get_ops(char *cmd,
+							char *param)
+{
+	return &eprobe_trigger_ops;
+}
+
+static struct event_command event_trigger_cmd = {
+	.name			= "eprobe",
+	.trigger_type		= ETT_EVENT_EPROBE,
+	.flags			= EVENT_CMD_FL_NEEDS_REC,
+	.func			= eprobe_trigger_cmd_func,
+	.reg			= eprobe_trigger_reg_func,
+	.unreg			= eprobe_trigger_unreg_func,
+	.unreg_all		= NULL,
+	.get_trigger_ops	= eprobe_trigger_get_ops,
+	.set_filter		= NULL,
+};
+
+static struct event_trigger_data *
+new_eprobe_trigger(struct trace_eprobe *ep, struct trace_event_file *file)
+{
+	struct event_trigger_data *trigger;
+	struct eprobe_data *edata;
+
+	edata = kzalloc(sizeof(*edata), GFP_KERNEL);
+	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+	if (!trigger || !edata) {
+		kfree(edata);
+		kfree(trigger);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	trigger->flags = EVENT_TRIGGER_FL_PROBE;
+	trigger->count = -1;
+	trigger->ops = &eprobe_trigger_ops;
+
+	/*
+	 * EVENT PROBE triggers are not registered as commands with
+	 * register_event_command(), as they are not controlled by the user
+	 * from the trigger file
+	 */
+	trigger->cmd_ops = &event_trigger_cmd;
+
+	INIT_LIST_HEAD(&trigger->list);
+	RCU_INIT_POINTER(trigger->filter, NULL);
+
+	edata->file = file;
+	edata->ep = ep;
+	trigger->private_data = edata;
+
+	return trigger;
+}
+
+static int enable_eprobe(struct trace_eprobe *ep,
+			 struct trace_event_file *eprobe_file)
+{
+	struct event_trigger_data *trigger;
+	struct trace_event_file *file;
+	struct trace_array *tr = eprobe_file->tr;
+
+	file = find_event_file(tr, ep->event_system, ep->event_name);
+	if (!file)
+		return -ENOENT;
+	trigger = new_eprobe_trigger(ep, eprobe_file);
+	if (IS_ERR(trigger))
+		return PTR_ERR(trigger);
+
+	list_add_tail_rcu(&trigger->list, &file->triggers);
+
+	trace_event_trigger_enable_disable(file, 1);
+	update_cond_flag(file);
+
+	return 0;
+}
+
+static struct trace_event_functions eprobe_funcs = {
+	.trace		= print_eprobe_event
+};
+
+static int disable_eprobe(struct trace_eprobe *ep,
+			  struct trace_array *tr)
+{
+	struct event_trigger_data *trigger;
+	struct trace_event_file *file;
+	struct eprobe_data *edata;
+
+	file = find_event_file(tr, ep->event_system, ep->event_name);
+	if (!file)
+		return -ENOENT;
+
+	list_for_each_entry(trigger, &file->triggers, list) {
+		if (!(trigger->flags & EVENT_TRIGGER_FL_PROBE))
+			continue;
+		edata = trigger->private_data;
+		if (edata->ep == ep)
+			break;
+	}
+	if (list_entry_is_head(trigger, &file->triggers, list))
+		return -ENODEV;
+
+	list_del_rcu(&trigger->list);
+
+	trace_event_trigger_enable_disable(file, 0);
+	update_cond_flag(file);
+	return 0;
+}
+
+static int enable_trace_eprobe(struct trace_event_call *call,
+			       struct trace_event_file *file)
+{
+	struct trace_probe *pos, *tp;
+	struct trace_eprobe *ep;
+	bool enabled;
+	int ret = 0;
+
+	tp = trace_probe_primary_from_call(call);
+	if (WARN_ON_ONCE(!tp))
+		return -ENODEV;
+	enabled = trace_probe_is_enabled(tp);
+
+	/* This also changes "enabled" state */
+	if (file) {
+		ret = trace_probe_add_file(tp, file);
+		if (ret)
+			return ret;
+	} else
+		trace_probe_set_flag(tp, TP_FLAG_PROFILE);
+
+	if (enabled)
+		return 0;
+
+	list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
+		ep = container_of(pos, struct trace_eprobe, tp);
+		ret = enable_eprobe(ep, file);
+		if (ret)
+			break;
+		enabled = true;
+	}
+
+	if (ret) {
+		/* Failed to enable one of them. Roll back all */
+		if (enabled)
+			disable_eprobe(ep, file->tr);
+		if (file)
+			trace_probe_remove_file(tp, file);
+		else
+			trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
+	}
+
+	return ret;
+}
+
+static int disable_trace_eprobe(struct trace_event_call *call,
+				struct trace_event_file *file)
+{
+	struct trace_probe *pos, *tp;
+	struct trace_eprobe *ep;
+
+	tp = trace_probe_primary_from_call(call);
+	if (WARN_ON_ONCE(!tp))
+		return -ENODEV;
+
+	if (file) {
+		if (!trace_probe_get_file_link(tp, file))
+			return -ENOENT;
+		if (!trace_probe_has_single_file(tp))
+			goto out;
+		trace_probe_clear_flag(tp, TP_FLAG_TRACE);
+	} else
+		trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
+
+	if (!trace_probe_is_enabled(tp)) {
+		list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
+			ep = container_of(pos, struct trace_eprobe, tp);
+			disable_eprobe(ep, file->tr);
+		}
+	}
+
+ out:
+	if (file)
+		/*
+		 * Synchronization is done in below function. For perf event,
+		 * file == NULL and perf_trace_event_unreg() calls
+		 * tracepoint_synchronize_unregister() to ensure synchronize
+		 * event. We don't need to care about it.
+		 */
+		trace_probe_remove_file(tp, file);
+
+	return 0;
+}
+
+static int eprobe_register(struct trace_event_call *event,
+			   enum trace_reg type, void *data)
+{
+	struct trace_event_file *file = data;
+
+	switch (type) {
+	case TRACE_REG_REGISTER:
+		return enable_trace_eprobe(event, file);
+	case TRACE_REG_UNREGISTER:
+		return disable_trace_eprobe(event, file);
+#ifdef CONFIG_PERF_EVENTS
+	case TRACE_REG_PERF_REGISTER:
+	case TRACE_REG_PERF_UNREGISTER:
+	case TRACE_REG_PERF_OPEN:
+	case TRACE_REG_PERF_CLOSE:
+	case TRACE_REG_PERF_ADD:
+	case TRACE_REG_PERF_DEL:
+		return 0;
+#endif
+	}
+	return 0;
+}
+
+static inline void init_trace_eprobe_call(struct trace_eprobe *ep)
+{
+	struct trace_event_call *call = trace_probe_event_call(&ep->tp);
+
+	call->flags = TRACE_EVENT_FL_EPROBE;
+	call->event.funcs = &eprobe_funcs;
+	call->class->fields_array = eprobe_fields_array;
+	call->class->reg = eprobe_register;
+}
+
+static struct trace_event_call *
+find_and_get_event(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) {
+		/* Skip other probes and ftrace events */
+		if (tp_event->flags &
+		    (TRACE_EVENT_FL_IGNORE_ENABLE |
+		     TRACE_EVENT_FL_KPROBE |
+		     TRACE_EVENT_FL_UPROBE |
+		     TRACE_EVENT_FL_EPROBE))
+			continue;
+		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;
+		if (!trace_event_try_get_ref(tp_event)) {
+			return NULL;
+			break;
+		}
+		return tp_event;
+		break;
+	}
+	return NULL;
+}
+
+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;
+	int ret;
+
+	ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], flags);
+	if (ret)
+		return ret;
+
+	if (ep->tp.args[i].code->op == FETCH_OP_TP_ARG)
+		ret = trace_eprobe_tp_arg_update(ep, i);
+
+	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;
+	}
+	if (!is_good_name(event) || !is_good_name(group))
+		goto parse_error;
+
+	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;
+	}
+	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) {
+		if (ret == -EEXIST) {
+			trace_probe_log_set_index(0);
+			trace_probe_log_err(0, EVENT_EXIST);
+		}
+		mutex_unlock(&event_mutex);
+		goto error;
+	}
+	ret = dyn_event_add(&ep->devent, &ep->tp.event->call);
+	mutex_unlock(&event_mutex);
+	return ret;
+parse_error:
+	ret = -EINVAL;
+error:
+	trace_event_probe_cleanup(ep);
+	return ret;
+}
+
+/*
+ * Register dynevent at core_initcall. This allows kernel to setup eprobe
+ * events in postcore_initcall without tracefs.
+ */
+static __init int trace_events_eprobe_init_early(void)
+{
+	int err = 0;
+
+	err = dyn_event_register(&eprobe_dyn_event_ops);
+	if (err)
+		pr_warn("Could not register eprobe_dyn_event_ops\n");
+
+	return err;
+}
+core_initcall(trace_events_eprobe_init_early);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 6b11e335a62e..3d5c07239a2a 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -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);
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 882c27044029..3a64ba4bbad6 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -707,14 +707,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[])
 {
 	/*
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 782c00eb6859..3ed2a3f37297 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -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;
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 66701a92d186..99e7a5df025e 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -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);
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases
  2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 1/5] tracing/probes: Reject events which have the same name of existing one Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 2/5] tracing: Add a probe that attaches to trace events Steven Rostedt
@ 2021-08-19 15:26 ` Steven Rostedt
  2021-08-20  0:13   ` Masami Hiramatsu
  2021-08-19 15:26 ` [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events Steven Rostedt
  2021-08-19 15:26 ` [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes Steven Rostedt
  4 siblings, 1 reply; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi, Shuah Khan, Shuah Khan, linux-kselftest

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

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.

Link: https://lkml.kernel.org/r/20210819041842.696873153@goodmis.org

Cc: Shuah Khan <shuah@kernel.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 .../testing/selftests/ftrace/test.d/functions | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions
index a6fac927ee82..f68d336b961b 100644
--- a/tools/testing/selftests/ftrace/test.d/functions
+++ b/tools/testing/selftests/ftrace/test.d/functions
@@ -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=`echo $line | 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
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events
  2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
                   ` (2 preceding siblings ...)
  2021-08-19 15:26 ` [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases Steven Rostedt
@ 2021-08-19 15:26 ` Steven Rostedt
  2021-08-20  0:46   ` Masami Hiramatsu
  2021-08-19 15:26 ` [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes Steven Rostedt
  4 siblings, 1 reply; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi, Shuah Khan, Shuah Khan, linux-kselftest

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

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.

Link: https://lkml.kernel.org/r/20210819041842.884828019@goodmis.org

Cc: Shuah Khan <shuah@kernel.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 .../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

diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
new file mode 100644
index 000000000000..7b242f29b916
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
@@ -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
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes
  2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
                   ` (3 preceding siblings ...)
  2021-08-19 15:26 ` [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events Steven Rostedt
@ 2021-08-19 15:26 ` Steven Rostedt
  2021-08-20  0:53   ` Masami Hiramatsu
  4 siblings, 1 reply; 12+ messages in thread
From: Steven Rostedt @ 2021-08-19 15:26 UTC (permalink / raw)
  To: linux-kernel, linux-trace-devel
  Cc: Ingo Molnar, Andrew Morton, Masami Hiramatsu, Tzvetomir Stoyanov,
	Tom Zanussi, Shuah Khan, Shuah Khan, linux-kselftest

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add a selftest that makes sure that eprobes and kprobes can not be created
with the same group and name as existing events.

Cc: Shuah Khan <shuah@kernel.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 .../ftrace/test.d/dynevent/test_duplicates.tc | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc

diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc
new file mode 100644
index 000000000000..022b569267ed
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc
@@ -0,0 +1,28 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Generic dynamic event - check if duplicate events are caught
+# requires: dynamic_events "e[:[<group>/]<event>] <attached-group>.<attached-event>o [<args>]":README
+
+echo 0 > events/enable
+
+clear_dynamic_events
+
+# first create dynamic events for eprobes and kprobes.
+
+echo 'e:egroup/eevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
+echo 'p:kgroup/kevent vfs_open file=+0($arg2)' >> dynamic_events
+
+# Test eprobe for same eprobe, existing kprobe and existing event
+! echo 'e:egroup/eevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
+! echo 'e:kgroup/kevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
+! echo 'e:syscalls/sys_enter_open syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
+
+# Test kprobe for same kprobe, existing eprobe and existing event
+! echo 'p:kgroup/kevent vfs_open file=+0($arg2)' >> dynamic_events
+! echo 'p:egroup/eevent vfs_open file=+0($arg2)' >> dynamic_events
+! echo 'p:syscalls/sys_enter_open vfs_open file=+0($arg2)' >> dynamic_events
+
+echo '-:egroup/eevent' >> dynamic_events
+echo '-:kgroup/kevent' >> dynamic_events
+
+clear_trace
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 2/5] tracing: Add a probe that attaches to trace events
  2021-08-19 15:26 ` [PATCH v8 2/5] tracing: Add a probe that attaches to trace events Steven Rostedt
@ 2021-08-20  0:10   ` Masami Hiramatsu
  0 siblings, 0 replies; 12+ messages in thread
From: Masami Hiramatsu @ 2021-08-20  0:10 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Tzvetomir Stoyanov, Tom Zanussi

On Thu, 19 Aug 2021 11:26:06 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: "Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com>
> 
> 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.
> 
> Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
> Link: https://lkml.kernel.org/r/20210819041842.485382601@goodmis.org
> 

This looks good to me.

Acked-by: Masami Hiramatsu <mhiramat@kernel.org>

Thank you!

> Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  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         | 903 ++++++++++++++++++++++++++++
>  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, 962 insertions(+), 13 deletions(-)
>  create mode 100644 kernel/trace/trace_eprobe.c
> 
> diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
> index 9564c4d9a3b6..0a0144580bbd 100644
> --- a/include/linux/trace_events.h
> +++ b/include/linux/trace_events.h
> @@ -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);
> diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
> index b1c47ccf4f73..6de5d4d63165 100644
> --- a/kernel/trace/Makefile
> +++ b/kernel/trace/Makefile
> @@ -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
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 8425c3d70895..489924cde4f8 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -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"
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 4a0e693000c6..b7c0f8e160fb 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -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.
>   *
> diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c
> new file mode 100644
> index 000000000000..56a96e9750cf
> --- /dev/null
> +++ b/kernel/trace/trace_eprobe.c
> @@ -0,0 +1,903 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * event probes
> + *
> + * Part of this code was copied from kernel/trace/trace_kprobe.c written by
> + * Masami Hiramatsu <mhiramat@kernel.org>
> + *
> + * Copyright (C) 2021, VMware Inc, Steven Rostedt <rostedt@goodmis.org>
> + * Copyright (C) 2021, VMware Inc, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/ftrace.h>
> +
> +#include "trace_dynevent.h"
> +#include "trace_probe.h"
> +#include "trace_probe_tmpl.h"
> +
> +#define EPROBE_EVENT_SYSTEM "eprobes"
> +
> +struct trace_eprobe {
> +	/* tracepoint system */
> +	const char *event_system;
> +
> +	/* tracepoint event */
> +	const char *event_name;
> +
> +	struct trace_event_call *event;
> +
> +	struct dyn_event	devent;
> +	struct trace_probe	tp;
> +};
> +
> +struct eprobe_data {
> +	struct trace_event_file	*file;
> +	struct trace_eprobe	*ep;
> +};
> +
> +static int __trace_eprobe_create(int argc, const char *argv[]);
> +
> +static void trace_event_probe_cleanup(struct trace_eprobe *ep)
> +{
> +	if (!ep)
> +		return;
> +	trace_probe_cleanup(&ep->tp);
> +	kfree(ep->event_name);
> +	kfree(ep->event_system);
> +	if (ep->event)
> +		trace_event_put_ref(ep->event);
> +	kfree(ep);
> +}
> +
> +static struct trace_eprobe *to_trace_eprobe(struct dyn_event *ev)
> +{
> +	return container_of(ev, struct trace_eprobe, devent);
> +}
> +
> +static int eprobe_dyn_event_create(const char *raw_command)
> +{
> +	return trace_probe_create(raw_command, __trace_eprobe_create);
> +}
> +
> +static int eprobe_dyn_event_show(struct seq_file *m, struct dyn_event *ev)
> +{
> +	struct trace_eprobe *ep = to_trace_eprobe(ev);
> +	int i;
> +
> +	seq_printf(m, "e:%s/%s", trace_probe_group_name(&ep->tp),
> +				trace_probe_name(&ep->tp));
> +	seq_printf(m, " %s.%s", ep->event_system, ep->event_name);
> +
> +	for (i = 0; i < ep->tp.nr_args; i++)
> +		seq_printf(m, " %s=%s", ep->tp.args[i].name, ep->tp.args[i].comm);
> +	seq_putc(m, '\n');
> +
> +	return 0;
> +}
> +
> +static int unregister_trace_eprobe(struct trace_eprobe *ep)
> +{
> +	/* If other probes are on the event, just unregister eprobe */
> +	if (trace_probe_has_sibling(&ep->tp))
> +		goto unreg;
> +
> +	/* Enabled event can not be unregistered */
> +	if (trace_probe_is_enabled(&ep->tp))
> +		return -EBUSY;
> +
> +	/* Will fail if probe is being used by ftrace or perf */
> +	if (trace_probe_unregister_event_call(&ep->tp))
> +		return -EBUSY;
> +
> +unreg:
> +	dyn_event_remove(&ep->devent);
> +	trace_probe_unlink(&ep->tp);
> +
> +	return 0;
> +}
> +
> +static int eprobe_dyn_event_release(struct dyn_event *ev)
> +{
> +	struct trace_eprobe *ep = to_trace_eprobe(ev);
> +	int ret = unregister_trace_eprobe(ep);
> +
> +	if (!ret)
> +		trace_event_probe_cleanup(ep);
> +	return ret;
> +}
> +
> +static bool eprobe_dyn_event_is_busy(struct dyn_event *ev)
> +{
> +	struct trace_eprobe *ep = to_trace_eprobe(ev);
> +
> +	return trace_probe_is_enabled(&ep->tp);
> +}
> +
> +static bool eprobe_dyn_event_match(const char *system, const char *event,
> +			int argc, const char **argv, struct dyn_event *ev)
> +{
> +	struct trace_eprobe *ep = to_trace_eprobe(ev);
> +
> +	return strcmp(trace_probe_name(&ep->tp), event) == 0 &&
> +	    (!system || strcmp(trace_probe_group_name(&ep->tp), system) == 0) &&
> +	    trace_probe_match_command_args(&ep->tp, argc, argv);
> +}
> +
> +static struct dyn_event_operations eprobe_dyn_event_ops = {
> +	.create = eprobe_dyn_event_create,
> +	.show = eprobe_dyn_event_show,
> +	.is_busy = eprobe_dyn_event_is_busy,
> +	.free = eprobe_dyn_event_release,
> +	.match = eprobe_dyn_event_match,
> +};
> +
> +static struct trace_eprobe *alloc_event_probe(const char *group,
> +					      const char *this_event,
> +					      struct trace_event_call *event,
> +					      int nargs)
> +{
> +	struct trace_eprobe *ep;
> +	const char *event_name;
> +	const char *sys_name;
> +	int ret = -ENOMEM;
> +
> +	if (!event)
> +		return ERR_PTR(-ENODEV);
> +
> +	sys_name = event->class->system;
> +	event_name = trace_event_name(event);
> +
> +	ep = kzalloc(struct_size(ep, tp.args, nargs), GFP_KERNEL);
> +	if (!ep) {
> +		trace_event_put_ref(ep->event);
> +		goto error;
> +	}
> +	ep->event = event;
> +	ep->event_name = kstrdup(event_name, GFP_KERNEL);
> +	if (!ep->event_name)
> +		goto error;
> +	ep->event_system = kstrdup(sys_name, GFP_KERNEL);
> +	if (!ep->event_system)
> +		goto error;
> +
> +	ret = trace_probe_init(&ep->tp, this_event, group, false);
> +	if (ret < 0)
> +		goto error;
> +
> +	dyn_event_init(&ep->devent, &eprobe_dyn_event_ops);
> +	return ep;
> +error:
> +	trace_event_probe_cleanup(ep);
> +	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;
> +
> +	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;
> +		}
> +	}
> +	kfree(parg->code->data);
> +	parg->code->data = NULL;
> +	return -ENOENT;
> +}
> +
> +static int eprobe_event_define_fields(struct trace_event_call *event_call)
> +{
> +	int ret;
> +	struct eprobe_trace_entry_head field;
> +	struct trace_probe *tp;
> +
> +	tp = trace_probe_primary_from_call(event_call);
> +	if (WARN_ON_ONCE(!tp))
> +		return -ENOENT;
> +
> +	DEFINE_FIELD(unsigned int, type, FIELD_STRING_TYPE, 0);
> +
> +	return traceprobe_define_arg_fields(event_call, sizeof(field), tp);
> +}
> +
> +static struct trace_event_fields eprobe_fields_array[] = {
> +	{ .type = TRACE_FUNCTION_TYPE,
> +	  .define_fields = eprobe_event_define_fields },
> +	{}
> +};
> +
> +/* Event entry printers */
> +static enum print_line_t
> +print_eprobe_event(struct trace_iterator *iter, int flags,
> +		   struct trace_event *event)
> +{
> +	struct eprobe_trace_entry_head *field;
> +	struct trace_event_call *pevent;
> +	struct trace_event *probed_event;
> +	struct trace_seq *s = &iter->seq;
> +	struct trace_probe *tp;
> +
> +	field = (struct eprobe_trace_entry_head *)iter->ent;
> +	tp = trace_probe_primary_from_call(
> +		container_of(event, struct trace_event_call, event));
> +	if (WARN_ON_ONCE(!tp))
> +		goto out;
> +
> +	trace_seq_printf(s, "%s: (", trace_probe_name(tp));
> +
> +	probed_event = ftrace_find_event(field->type);
> +	if (probed_event) {
> +		pevent = container_of(probed_event, struct trace_event_call, event);
> +		trace_seq_printf(s, "%s.%s", pevent->class->system,
> +				 trace_event_name(pevent));
> +	} else {
> +		trace_seq_printf(s, "%u", field->type);
> +	}
> +
> +	trace_seq_putc(s, ')');
> +
> +	if (print_probe_args(s, tp->args, tp->nr_args,
> +			     (u8 *)&field[1], field) < 0)
> +		goto out;
> +
> +	trace_seq_putc(s, '\n');
> + out:
> +	return trace_handle_return(s);
> +}
> +
> +static unsigned long get_event_field(struct fetch_insn *code, void *rec)
> +{
> +	struct ftrace_event_field *field = code->data;
> +	unsigned long val;
> +	void *addr;
> +
> +	addr = rec + field->offset;
> +
> +	switch (field->size) {
> +	case 1:
> +		if (field->is_signed)
> +			val = *(char *)addr;
> +		else
> +			val = *(unsigned char *)addr;
> +		break;
> +	case 2:
> +		if (field->is_signed)
> +			val = *(short *)addr;
> +		else
> +			val = *(unsigned short *)addr;
> +		break;
> +	case 4:
> +		if (field->is_signed)
> +			val = *(int *)addr;
> +		else
> +			val = *(unsigned int *)addr;
> +		break;
> +	default:
> +		if (field->is_signed)
> +			val = *(long *)addr;
> +		else
> +			val = *(unsigned long *)addr;
> +		break;
> +	}
> +	return val;
> +}
> +
> +static int get_eprobe_size(struct trace_probe *tp, void *rec)
> +{
> +	struct probe_arg *arg;
> +	int i, len, ret = 0;
> +
> +	for (i = 0; i < tp->nr_args; i++) {
> +		arg = tp->args + i;
> +		if (unlikely(arg->dynamic)) {
> +			unsigned long val;
> +
> +			val = get_event_field(arg->code, rec);
> +			len = process_fetch_insn_bottom(arg->code + 1, val, NULL, NULL);
> +			if (len > 0)
> +				ret += len;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +/* Kprobe specific fetch functions */
> +
> +/* 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, void *rec, void *dest,
> +		   void *base)
> +{
> +	unsigned long val;
> +
> +	val = get_event_field(code, rec);
> +	return process_fetch_insn_bottom(code + 1, val, dest, base);
> +}
> +NOKPROBE_SYMBOL(process_fetch_insn)
> +
> +/* Return the length of string -- including null terminal byte */
> +static nokprobe_inline int
> +fetch_store_strlen_user(unsigned long addr)
> +{
> +	const void __user *uaddr =  (__force const void __user *)addr;
> +
> +	return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
> +}
> +
> +/* Return the length of string -- including null terminal byte */
> +static nokprobe_inline int
> +fetch_store_strlen(unsigned long addr)
> +{
> +	int ret, len = 0;
> +	u8 c;
> +
> +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
> +	if (addr < TASK_SIZE)
> +		return fetch_store_strlen_user(addr);
> +#endif
> +
> +	do {
> +		ret = copy_from_kernel_nofault(&c, (u8 *)addr + len, 1);
> +		len++;
> +	} while (c && ret == 0 && len < MAX_STRING_SIZE);
> +
> +	return (ret < 0) ? ret : len;
> +}
> +
> +/*
> + * Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf
> + * with max length and relative data location.
> + */
> +static nokprobe_inline int
> +fetch_store_string_user(unsigned long addr, void *dest, void *base)
> +{
> +	const void __user *uaddr =  (__force const void __user *)addr;
> +	int maxlen = get_loc_len(*(u32 *)dest);
> +	void *__dest;
> +	long ret;
> +
> +	if (unlikely(!maxlen))
> +		return -ENOMEM;
> +
> +	__dest = get_loc_data(dest, base);
> +
> +	ret = strncpy_from_user_nofault(__dest, uaddr, maxlen);
> +	if (ret >= 0)
> +		*(u32 *)dest = make_data_loc(ret, __dest - base);
> +
> +	return ret;
> +}
> +
> +/*
> + * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
> + * length and relative data location.
> + */
> +static nokprobe_inline int
> +fetch_store_string(unsigned long addr, void *dest, void *base)
> +{
> +	int maxlen = get_loc_len(*(u32 *)dest);
> +	void *__dest;
> +	long ret;
> +
> +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
> +	if ((unsigned long)addr < TASK_SIZE)
> +		return fetch_store_string_user(addr, dest, base);
> +#endif
> +
> +	if (unlikely(!maxlen))
> +		return -ENOMEM;
> +
> +	__dest = get_loc_data(dest, base);
> +
> +	/*
> +	 * Try to get string again, since the string can be changed while
> +	 * probing.
> +	 */
> +	ret = strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen);
> +	if (ret >= 0)
> +		*(u32 *)dest = make_data_loc(ret, __dest - base);
> +
> +	return ret;
> +}
> +
> +static nokprobe_inline int
> +probe_mem_read_user(void *dest, void *src, size_t size)
> +{
> +	const void __user *uaddr =  (__force const void __user *)src;
> +
> +	return copy_from_user_nofault(dest, uaddr, size);
> +}
> +
> +static nokprobe_inline int
> +probe_mem_read(void *dest, void *src, size_t size)
> +{
> +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
> +	if ((unsigned long)src < TASK_SIZE)
> +		return probe_mem_read_user(dest, src, size);
> +#endif
> +	return copy_from_kernel_nofault(dest, src, size);
> +}
> +
> +/* eprobe handler */
> +static inline void
> +__eprobe_trace_func(struct eprobe_data *edata, void *rec)
> +{
> +	struct eprobe_trace_entry_head *entry;
> +	struct trace_event_call *call = trace_probe_event_call(&edata->ep->tp);
> +	struct trace_event_buffer fbuffer;
> +	int dsize;
> +
> +	if (WARN_ON_ONCE(call != edata->file->event_call))
> +		return;
> +
> +	if (trace_trigger_soft_disabled(edata->file))
> +		return;
> +
> +	fbuffer.trace_ctx = tracing_gen_ctx();
> +	fbuffer.trace_file = edata->file;
> +
> +	dsize = get_eprobe_size(&edata->ep->tp, rec);
> +	fbuffer.regs = NULL;
> +
> +	fbuffer.event =
> +		trace_event_buffer_lock_reserve(&fbuffer.buffer, edata->file,
> +					call->event.type,
> +					sizeof(*entry) + edata->ep->tp.size + dsize,
> +					fbuffer.trace_ctx);
> +	if (!fbuffer.event)
> +		return;
> +
> +	entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
> +	if (edata->ep->event)
> +		entry->type = edata->ep->event->event.type;
> +	else
> +		entry->type = 0;
> +	store_trace_args(&entry[1], &edata->ep->tp, rec, sizeof(*entry), dsize);
> +
> +	trace_event_buffer_commit(&fbuffer);
> +}
> +
> +/*
> + * The event probe implementation uses event triggers to get access to
> + * the event it is attached to, but is not an actual trigger. The below
> + * functions are just stubs to fulfill what is needed to use the trigger
> + * infrastructure.
> + */
> +static int eprobe_trigger_init(struct event_trigger_ops *ops,
> +			       struct event_trigger_data *data)
> +{
> +	return 0;
> +}
> +
> +static void eprobe_trigger_free(struct event_trigger_ops *ops,
> +				struct event_trigger_data *data)
> +{
> +
> +}
> +
> +static int eprobe_trigger_print(struct seq_file *m,
> +				struct event_trigger_ops *ops,
> +				struct event_trigger_data *data)
> +{
> +	/* Do not print eprobe event triggers */
> +	return 0;
> +}
> +
> +static void eprobe_trigger_func(struct event_trigger_data *data,
> +				struct trace_buffer *buffer, void *rec,
> +				struct ring_buffer_event *rbe)
> +{
> +	struct eprobe_data *edata = data->private_data;
> +
> +	__eprobe_trace_func(edata, rec);
> +}
> +
> +static struct event_trigger_ops eprobe_trigger_ops = {
> +	.func			= eprobe_trigger_func,
> +	.print			= eprobe_trigger_print,
> +	.init			= eprobe_trigger_init,
> +	.free			= eprobe_trigger_free,
> +};
> +
> +static int eprobe_trigger_cmd_func(struct event_command *cmd_ops,
> +				   struct trace_event_file *file,
> +				   char *glob, char *cmd, char *param)
> +{
> +	return -1;
> +}
> +
> +static int eprobe_trigger_reg_func(char *glob, struct event_trigger_ops *ops,
> +				 struct event_trigger_data *data,
> +				 struct trace_event_file *file)
> +{
> +	return -1;
> +}
> +
> +static void eprobe_trigger_unreg_func(char *glob, struct event_trigger_ops *ops,
> +				    struct event_trigger_data *data,
> +				    struct trace_event_file *file)
> +{
> +
> +}
> +
> +static struct event_trigger_ops *eprobe_trigger_get_ops(char *cmd,
> +							char *param)
> +{
> +	return &eprobe_trigger_ops;
> +}
> +
> +static struct event_command event_trigger_cmd = {
> +	.name			= "eprobe",
> +	.trigger_type		= ETT_EVENT_EPROBE,
> +	.flags			= EVENT_CMD_FL_NEEDS_REC,
> +	.func			= eprobe_trigger_cmd_func,
> +	.reg			= eprobe_trigger_reg_func,
> +	.unreg			= eprobe_trigger_unreg_func,
> +	.unreg_all		= NULL,
> +	.get_trigger_ops	= eprobe_trigger_get_ops,
> +	.set_filter		= NULL,
> +};
> +
> +static struct event_trigger_data *
> +new_eprobe_trigger(struct trace_eprobe *ep, struct trace_event_file *file)
> +{
> +	struct event_trigger_data *trigger;
> +	struct eprobe_data *edata;
> +
> +	edata = kzalloc(sizeof(*edata), GFP_KERNEL);
> +	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
> +	if (!trigger || !edata) {
> +		kfree(edata);
> +		kfree(trigger);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	trigger->flags = EVENT_TRIGGER_FL_PROBE;
> +	trigger->count = -1;
> +	trigger->ops = &eprobe_trigger_ops;
> +
> +	/*
> +	 * EVENT PROBE triggers are not registered as commands with
> +	 * register_event_command(), as they are not controlled by the user
> +	 * from the trigger file
> +	 */
> +	trigger->cmd_ops = &event_trigger_cmd;
> +
> +	INIT_LIST_HEAD(&trigger->list);
> +	RCU_INIT_POINTER(trigger->filter, NULL);
> +
> +	edata->file = file;
> +	edata->ep = ep;
> +	trigger->private_data = edata;
> +
> +	return trigger;
> +}
> +
> +static int enable_eprobe(struct trace_eprobe *ep,
> +			 struct trace_event_file *eprobe_file)
> +{
> +	struct event_trigger_data *trigger;
> +	struct trace_event_file *file;
> +	struct trace_array *tr = eprobe_file->tr;
> +
> +	file = find_event_file(tr, ep->event_system, ep->event_name);
> +	if (!file)
> +		return -ENOENT;
> +	trigger = new_eprobe_trigger(ep, eprobe_file);
> +	if (IS_ERR(trigger))
> +		return PTR_ERR(trigger);
> +
> +	list_add_tail_rcu(&trigger->list, &file->triggers);
> +
> +	trace_event_trigger_enable_disable(file, 1);
> +	update_cond_flag(file);
> +
> +	return 0;
> +}
> +
> +static struct trace_event_functions eprobe_funcs = {
> +	.trace		= print_eprobe_event
> +};
> +
> +static int disable_eprobe(struct trace_eprobe *ep,
> +			  struct trace_array *tr)
> +{
> +	struct event_trigger_data *trigger;
> +	struct trace_event_file *file;
> +	struct eprobe_data *edata;
> +
> +	file = find_event_file(tr, ep->event_system, ep->event_name);
> +	if (!file)
> +		return -ENOENT;
> +
> +	list_for_each_entry(trigger, &file->triggers, list) {
> +		if (!(trigger->flags & EVENT_TRIGGER_FL_PROBE))
> +			continue;
> +		edata = trigger->private_data;
> +		if (edata->ep == ep)
> +			break;
> +	}
> +	if (list_entry_is_head(trigger, &file->triggers, list))
> +		return -ENODEV;
> +
> +	list_del_rcu(&trigger->list);
> +
> +	trace_event_trigger_enable_disable(file, 0);
> +	update_cond_flag(file);
> +	return 0;
> +}
> +
> +static int enable_trace_eprobe(struct trace_event_call *call,
> +			       struct trace_event_file *file)
> +{
> +	struct trace_probe *pos, *tp;
> +	struct trace_eprobe *ep;
> +	bool enabled;
> +	int ret = 0;
> +
> +	tp = trace_probe_primary_from_call(call);
> +	if (WARN_ON_ONCE(!tp))
> +		return -ENODEV;
> +	enabled = trace_probe_is_enabled(tp);
> +
> +	/* This also changes "enabled" state */
> +	if (file) {
> +		ret = trace_probe_add_file(tp, file);
> +		if (ret)
> +			return ret;
> +	} else
> +		trace_probe_set_flag(tp, TP_FLAG_PROFILE);
> +
> +	if (enabled)
> +		return 0;
> +
> +	list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
> +		ep = container_of(pos, struct trace_eprobe, tp);
> +		ret = enable_eprobe(ep, file);
> +		if (ret)
> +			break;
> +		enabled = true;
> +	}
> +
> +	if (ret) {
> +		/* Failed to enable one of them. Roll back all */
> +		if (enabled)
> +			disable_eprobe(ep, file->tr);
> +		if (file)
> +			trace_probe_remove_file(tp, file);
> +		else
> +			trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
> +	}
> +
> +	return ret;
> +}
> +
> +static int disable_trace_eprobe(struct trace_event_call *call,
> +				struct trace_event_file *file)
> +{
> +	struct trace_probe *pos, *tp;
> +	struct trace_eprobe *ep;
> +
> +	tp = trace_probe_primary_from_call(call);
> +	if (WARN_ON_ONCE(!tp))
> +		return -ENODEV;
> +
> +	if (file) {
> +		if (!trace_probe_get_file_link(tp, file))
> +			return -ENOENT;
> +		if (!trace_probe_has_single_file(tp))
> +			goto out;
> +		trace_probe_clear_flag(tp, TP_FLAG_TRACE);
> +	} else
> +		trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
> +
> +	if (!trace_probe_is_enabled(tp)) {
> +		list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
> +			ep = container_of(pos, struct trace_eprobe, tp);
> +			disable_eprobe(ep, file->tr);
> +		}
> +	}
> +
> + out:
> +	if (file)
> +		/*
> +		 * Synchronization is done in below function. For perf event,
> +		 * file == NULL and perf_trace_event_unreg() calls
> +		 * tracepoint_synchronize_unregister() to ensure synchronize
> +		 * event. We don't need to care about it.
> +		 */
> +		trace_probe_remove_file(tp, file);
> +
> +	return 0;
> +}
> +
> +static int eprobe_register(struct trace_event_call *event,
> +			   enum trace_reg type, void *data)
> +{
> +	struct trace_event_file *file = data;
> +
> +	switch (type) {
> +	case TRACE_REG_REGISTER:
> +		return enable_trace_eprobe(event, file);
> +	case TRACE_REG_UNREGISTER:
> +		return disable_trace_eprobe(event, file);
> +#ifdef CONFIG_PERF_EVENTS
> +	case TRACE_REG_PERF_REGISTER:
> +	case TRACE_REG_PERF_UNREGISTER:
> +	case TRACE_REG_PERF_OPEN:
> +	case TRACE_REG_PERF_CLOSE:
> +	case TRACE_REG_PERF_ADD:
> +	case TRACE_REG_PERF_DEL:
> +		return 0;
> +#endif
> +	}
> +	return 0;
> +}
> +
> +static inline void init_trace_eprobe_call(struct trace_eprobe *ep)
> +{
> +	struct trace_event_call *call = trace_probe_event_call(&ep->tp);
> +
> +	call->flags = TRACE_EVENT_FL_EPROBE;
> +	call->event.funcs = &eprobe_funcs;
> +	call->class->fields_array = eprobe_fields_array;
> +	call->class->reg = eprobe_register;
> +}
> +
> +static struct trace_event_call *
> +find_and_get_event(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) {
> +		/* Skip other probes and ftrace events */
> +		if (tp_event->flags &
> +		    (TRACE_EVENT_FL_IGNORE_ENABLE |
> +		     TRACE_EVENT_FL_KPROBE |
> +		     TRACE_EVENT_FL_UPROBE |
> +		     TRACE_EVENT_FL_EPROBE))
> +			continue;
> +		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;
> +		if (!trace_event_try_get_ref(tp_event)) {
> +			return NULL;
> +			break;
> +		}
> +		return tp_event;
> +		break;
> +	}
> +	return NULL;
> +}
> +
> +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;
> +	int ret;
> +
> +	ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], flags);
> +	if (ret)
> +		return ret;
> +
> +	if (ep->tp.args[i].code->op == FETCH_OP_TP_ARG)
> +		ret = trace_eprobe_tp_arg_update(ep, i);
> +
> +	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;
> +	}
> +	if (!is_good_name(event) || !is_good_name(group))
> +		goto parse_error;
> +
> +	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;
> +	}
> +	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) {
> +		if (ret == -EEXIST) {
> +			trace_probe_log_set_index(0);
> +			trace_probe_log_err(0, EVENT_EXIST);
> +		}
> +		mutex_unlock(&event_mutex);
> +		goto error;
> +	}
> +	ret = dyn_event_add(&ep->devent, &ep->tp.event->call);
> +	mutex_unlock(&event_mutex);
> +	return ret;
> +parse_error:
> +	ret = -EINVAL;
> +error:
> +	trace_event_probe_cleanup(ep);
> +	return ret;
> +}
> +
> +/*
> + * Register dynevent at core_initcall. This allows kernel to setup eprobe
> + * events in postcore_initcall without tracefs.
> + */
> +static __init int trace_events_eprobe_init_early(void)
> +{
> +	int err = 0;
> +
> +	err = dyn_event_register(&eprobe_dyn_event_ops);
> +	if (err)
> +		pr_warn("Could not register eprobe_dyn_event_ops\n");
> +
> +	return err;
> +}
> +core_initcall(trace_events_eprobe_init_early);
> diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
> index 6b11e335a62e..3d5c07239a2a 100644
> --- a/kernel/trace/trace_events_trigger.c
> +++ b/kernel/trace/trace_events_trigger.c
> @@ -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);
> diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
> index 882c27044029..3a64ba4bbad6 100644
> --- a/kernel/trace/trace_kprobe.c
> +++ b/kernel/trace/trace_kprobe.c
> @@ -707,14 +707,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[])
>  {
>  	/*
> diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
> index 782c00eb6859..3ed2a3f37297 100644
> --- a/kernel/trace/trace_probe.c
> +++ b/kernel/trace/trace_probe.c
> @@ -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;
> diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
> index 66701a92d186..99e7a5df025e 100644
> --- a/kernel/trace/trace_probe.h
> +++ b/kernel/trace/trace_probe.h
> @@ -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);
> -- 
> 2.30.2


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases
  2021-08-19 15:26 ` [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases Steven Rostedt
@ 2021-08-20  0:13   ` Masami Hiramatsu
  0 siblings, 0 replies; 12+ messages in thread
From: Masami Hiramatsu @ 2021-08-20  0:13 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Tzvetomir Stoyanov, Tom Zanussi, Shuah Khan,
	Shuah Khan, linux-kselftest

On Thu, 19 Aug 2021 11:26:07 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> 
> 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.
> 
> Link: https://lkml.kernel.org/r/20210819041842.696873153@goodmis.org
> 

Thank you for adding this useful function!

Acked-by: Masami Hiramatsu <mhiramat@kernel.org>


> Cc: Shuah Khan <shuah@kernel.org>
> Cc: Shuah Khan <skhan@linuxfoundation.org>
> Cc: linux-kselftest@vger.kernel.org
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  .../testing/selftests/ftrace/test.d/functions | 22 +++++++++++++++++++
>  1 file changed, 22 insertions(+)
> 
> diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions
> index a6fac927ee82..f68d336b961b 100644
> --- a/tools/testing/selftests/ftrace/test.d/functions
> +++ b/tools/testing/selftests/ftrace/test.d/functions
> @@ -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=`echo $line | 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
> -- 
> 2.30.2


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events
  2021-08-19 15:26 ` [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events Steven Rostedt
@ 2021-08-20  0:46   ` Masami Hiramatsu
  2021-08-20 14:08     ` Steven Rostedt
  0 siblings, 1 reply; 12+ messages in thread
From: Masami Hiramatsu @ 2021-08-20  0:46 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Tzvetomir Stoyanov, Tom Zanussi, Shuah Khan,
	Shuah Khan, linux-kselftest

On Thu, 19 Aug 2021 11:26:08 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> 
> 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.
> 
> Link: https://lkml.kernel.org/r/20210819041842.884828019@goodmis.org
> 
> Cc: Shuah Khan <shuah@kernel.org>
> Cc: Shuah Khan <skhan@linuxfoundation.org>
> Cc: linux-kselftest@vger.kernel.org
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  .../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
> 
> diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
> new file mode 100644
> index 000000000000..7b242f29b916
> --- /dev/null
> +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
> @@ -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

Hmm, can you make this more simple one without synthetic events?
Since synthetic event depends on CONFIG_SYNTH_EVENTS, you need to add
"synth_events" to 'requires' tag.
However, this means that this testcase doesn't run when CONFIG_SYNTH_EVENTS=n
but CONFIG_*PROBE_EVENTS=y.

Thank you,

> +
> +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
> -- 
> 2.30.2


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes
  2021-08-19 15:26 ` [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes Steven Rostedt
@ 2021-08-20  0:53   ` Masami Hiramatsu
  2021-08-20 14:13     ` Steven Rostedt
  0 siblings, 1 reply; 12+ messages in thread
From: Masami Hiramatsu @ 2021-08-20  0:53 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Tzvetomir Stoyanov, Tom Zanussi, Shuah Khan,
	Shuah Khan, linux-kselftest

On Thu, 19 Aug 2021 11:26:09 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
> 
> Add a selftest that makes sure that eprobes and kprobes can not be created
> with the same group and name as existing events.

Hmm, can you also remove kprobe event dependency from this test case?
The event probe depends on CONFIG_PROBE_EVENTS meta config, but that
is enabled both of CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS.
This means if the kernel configs CONFIG_KPROBE_EVENTS=n and
CONFIG_UPROBE_EVENTS=y, this will be executed but should fail.

Thank you,

> 
> Cc: Shuah Khan <shuah@kernel.org>
> Cc: Shuah Khan <skhan@linuxfoundation.org>
> Cc: linux-kselftest@vger.kernel.org
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> ---
>  .../ftrace/test.d/dynevent/test_duplicates.tc | 28 +++++++++++++++++++
>  1 file changed, 28 insertions(+)
>  create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc
> 
> diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc
> new file mode 100644
> index 000000000000..022b569267ed
> --- /dev/null
> +++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc
> @@ -0,0 +1,28 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +# description: Generic dynamic event - check if duplicate events are caught
> +# requires: dynamic_events "e[:[<group>/]<event>] <attached-group>.<attached-event>o [<args>]":README
> +
> +echo 0 > events/enable
> +
> +clear_dynamic_events
> +
> +# first create dynamic events for eprobes and kprobes.
> +
> +echo 'e:egroup/eevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
> +echo 'p:kgroup/kevent vfs_open file=+0($arg2)' >> dynamic_events
> +
> +# Test eprobe for same eprobe, existing kprobe and existing event
> +! echo 'e:egroup/eevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
> +! echo 'e:kgroup/kevent syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
> +! echo 'e:syscalls/sys_enter_open syscalls/sys_enter_openat file=+0($filename):ustring' >> dynamic_events
> +
> +# Test kprobe for same kprobe, existing eprobe and existing event
> +! echo 'p:kgroup/kevent vfs_open file=+0($arg2)' >> dynamic_events
> +! echo 'p:egroup/eevent vfs_open file=+0($arg2)' >> dynamic_events
> +! echo 'p:syscalls/sys_enter_open vfs_open file=+0($arg2)' >> dynamic_events
> +
> +echo '-:egroup/eevent' >> dynamic_events
> +echo '-:kgroup/kevent' >> dynamic_events
> +
> +clear_trace
> -- 
> 2.30.2


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events
  2021-08-20  0:46   ` Masami Hiramatsu
@ 2021-08-20 14:08     ` Steven Rostedt
  0 siblings, 0 replies; 12+ messages in thread
From: Steven Rostedt @ 2021-08-20 14:08 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Tzvetomir Stoyanov, Tom Zanussi, Shuah Khan, Shuah Khan,
	linux-kselftest

On Fri, 20 Aug 2021 09:46:39 +0900
Masami Hiramatsu <mhiramat@kernel.org> wrote:

> > +
> > +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  
> 
> Hmm, can you make this more simple one without synthetic events?
> Since synthetic event depends on CONFIG_SYNTH_EVENTS, you need to add
> "synth_events" to 'requires' tag.
> However, this means that this testcase doesn't run when CONFIG_SYNTH_EVENTS=n
> but CONFIG_*PROBE_EVENTS=y.

Sure. I just used this because it was one of the main purposes I was
using it for. I may keep this as a test, but will rename it as
something else, and make a more simple one for just testing the event
probe add and removal.

-- Steve

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes
  2021-08-20  0:53   ` Masami Hiramatsu
@ 2021-08-20 14:13     ` Steven Rostedt
  0 siblings, 0 replies; 12+ messages in thread
From: Steven Rostedt @ 2021-08-20 14:13 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, linux-trace-devel, Ingo Molnar, Andrew Morton,
	Tzvetomir Stoyanov, Tom Zanussi, Shuah Khan, Shuah Khan,
	linux-kselftest

On Fri, 20 Aug 2021 09:53:59 +0900
Masami Hiramatsu <mhiramat@kernel.org> wrote:

> Hmm, can you also remove kprobe event dependency from this test case?
> The event probe depends on CONFIG_PROBE_EVENTS meta config, but that
> is enabled both of CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS.
> This means if the kernel configs CONFIG_KPROBE_EVENTS=n and
> CONFIG_UPROBE_EVENTS=y, this will be executed but should fail.

I'll add a check if kprobes are available before testing the kprobes.

-- Steve

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2021-08-20 14:13 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-19 15:26 [PATCH v8 0/5] tracing: Creation of event probe Steven Rostedt
2021-08-19 15:26 ` [PATCH v8 1/5] tracing/probes: Reject events which have the same name of existing one Steven Rostedt
2021-08-19 15:26 ` [PATCH v8 2/5] tracing: Add a probe that attaches to trace events Steven Rostedt
2021-08-20  0:10   ` Masami Hiramatsu
2021-08-19 15:26 ` [PATCH v8 3/5] selftests/ftrace: Add clear_dynamic_events() to test cases Steven Rostedt
2021-08-20  0:13   ` Masami Hiramatsu
2021-08-19 15:26 ` [PATCH v8 4/5] selftests/ftrace: Add selftest for testing eprobe events Steven Rostedt
2021-08-20  0:46   ` Masami Hiramatsu
2021-08-20 14:08     ` Steven Rostedt
2021-08-19 15:26 ` [PATCH v8 5/5] selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes Steven Rostedt
2021-08-20  0:53   ` Masami Hiramatsu
2021-08-20 14:13     ` Steven Rostedt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).