Linux-Trace-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v2 0/4] libtracefs: Creating synthetic events
@ 2021-07-22 13:36 Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 1/4] libtracefs: Add tracefs_list_pop() to remove the last item Steven Rostedt
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Steven Rostedt @ 2021-07-22 13:36 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	Daniel Bristot de Oliveira, Steven Rostedt (VMware)

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

Add an interface to libtracefs that makes it easy to create synthetic
events. For example:

	#define start_event "sched_waking"
	#define start_field "pid"

	#define end_event "sched_switch"
	#define end_field "next_pid"

	#define match_name "pid"


	struct tracefs_synth *synth;
	struct tep_handle *tep;
	struct trace_seq seq;

	/* Load all events from the system */
	tep = tracefs_local_events(NULL);
	
	/* Initialize the synthetic event */
	synth = tracefs_synth_init(tep, "wakeup_lat",
				   NULL, start_event,
				   NULL, end_event,
				   start_field, end_field,
				   match_name);

	/* The tep is no longer needed */
	tep_free(tep);


	/* Save the "prio" field as "prio" from the start event */
	tracefs_synth_add_start_field(synth, "prio", NULL);

	/* Save the "next_comm" as "comm" from the end event */
	tracefs_synth_add_end_field(synth, "next_comm", "comm");

	/* Save the "prev_prio" as "prev_prio" from the end event */
	tracefs_synth_add_end_field(synth, "prev_prio", NULL);

	/*
	 * Take a microsecond time difference between end and start
	 * and record as "delta"
	 */
	tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
					TRACEFS_TIMESTAMP_USECS,
					TRACEFS_SYNTH_DELTA_END, "delta");

	/* Only record if start event "prio" is less than 100 */
	tracefs_synth_add_start_filter(synth, "prio",
				       TRACEFS_COMPARE_LT, "100",
				       false, false);

	/*
	 * Only record if end event "next_prio" is less than 50
	 * or the previous task's prio was less than 100.
	 */
	tracefs_synth_add_end_filter(synth, "next_prio",
				       TRACEFS_COMPARE_LT, "50",
				       false, false);
	tracefs_synth_add_end_filter(synth, "prev_prio",
				       TRACEFS_COMPARE_LT, "100",
				       false, true);

	trace_seq_init(&seq);
	tracefs_synth_show(&seq, tracefs_synth_show(&s, NULL, synth);
	trace_seq_terminate(&s);
	trace_seq_do_printf(&s);
	trace_seq_destroy(&s);

	tracefs_synth_free(synth);

Will produce:

echo 'wakeup_lat s32 pid; s32 prio; char comm[16]; s32 prev_prio; u64 delta;' > /sys/kernel/tracing/synthetic_events
echo 'hist:keys=pid:__arg__1=prio,__arg__2=common_timestamp.usecs if (prio < 100)' > /sys/kernel/tracing/events/sched/sched_waking/trigger
echo 'hist:keys=next_pid:pid=next_pid,prio=$__arg__1,comm=next_comm,prev_prio=prev_prio,delta=common_timestamp.usecs-$__arg__2:onmatch(sched.sched_waking).trace(wakeup_lat,$pid,$prio,$comm,$prev_prio,$delta) if (next_prio < 50) || (prev_prio < 100)' > /sys/kernel/tracing/events/sched/sched_switch/trigger

There's functionality to also create and destroy the synthetic event, not
only produce the above.

Once this is in libtracefs, it can then be brought over to trace-cruncher
where this can be done in Python.

Changes since v1:

  After playing with this a bit, I found that having the synthetic name of
  the fields first awkward. That is, from the above:

	tracefs_synth_add_start_field(synth, "prio", NULL);
	tracefs_synth_add_end_field(synth, "next_comm", "comm");
	tracefs_synth_add_end_field(synth, "prev_prio", NULL);

  Looks and feels better than:

	tracefs_synth_add_start_field(synth, NULL, "prio");
	tracefs_synth_add_end_field(synth, "comm", "next_comm");
	tracefs_synth_add_end_field(synth, NULL, "prev_prio");

 That's because, in many cases it is optional, and may be NULL,
 which looks better at the end than in the middle.

Steven Rostedt (VMware) (4):
  libtracefs: Add tracefs_list_pop() to remove the last item
  libtracefs: Create a way to create a synthetic event
  libtracefs: Add TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS to synth
  libtracefs: Add man pages for creating synthetic events

 Documentation/libtracefs-synth.txt |  353 +++++++++
 include/tracefs.h                  |   70 ++
 src/tracefs-hist.c                 | 1103 ++++++++++++++++++++++++++++
 src/tracefs-utils.c                |   26 +
 4 files changed, 1552 insertions(+)
 create mode 100644 Documentation/libtracefs-synth.txt

-- 
2.30.2


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

* [PATCH v2 1/4] libtracefs: Add tracefs_list_pop() to remove the last item
  2021-07-22 13:36 [PATCH v2 0/4] libtracefs: Creating synthetic events Steven Rostedt
@ 2021-07-22 13:36 ` Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 2/4] libtracefs: Create a way to create a synthetic event Steven Rostedt
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2021-07-22 13:36 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	Daniel Bristot de Oliveira, Steven Rostedt (VMware)

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

Add a way to remove the last item of a string list. This will be needed to
revert changes made to the new tracefs_synth structure that will have more
than one tracefs_list char ** that must be the same size. If one is
successfully updated, but the next one fails, the first one needs to be
reverted.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h   |  1 +
 src/tracefs-utils.c | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index 2649417654b1..4dd77b9c4119 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -66,6 +66,7 @@ int tracefs_error_clear(struct tracefs_instance *instance);
 void tracefs_list_free(char **list);
 char **tracefs_list_add(char **list, const char *string);
 int tracefs_list_size(char **list);
+int tracefs_list_pop(char **list);
 
 /**
  * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
index 600cdf2acefc..63bb413298fe 100644
--- a/src/tracefs-utils.c
+++ b/src/tracefs-utils.c
@@ -448,6 +448,32 @@ char **tracefs_list_add(char **list, const char *string)
 	return list;
 }
 
+/**
+ * tracefs_list_pop - Removes the last string added
+ * @list: The list to remove the last event from
+ *
+ * Returns 0 on success, -1 on error.
+ * Returns 1 if the list is empty or NULL.
+ */
+int tracefs_list_pop(char **list)
+{
+	unsigned long size;
+
+	if (!list || list[0])
+		return 1;
+
+	list--;
+	size = *(unsigned long *)list;
+	/* size must be greater than zero */
+	if (!size)
+		return -1;
+	size--;
+	*list = (char *)size;
+	list++;
+	list[size] = '\0';
+	return 0;
+}
+
 /**
  * tracefs_list_size - Return the number of strings in the list
  * @list: The list to determine the size.
-- 
2.30.2


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

* [PATCH v2 2/4] libtracefs: Create a way to create a synthetic event
  2021-07-22 13:36 [PATCH v2 0/4] libtracefs: Creating synthetic events Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 1/4] libtracefs: Add tracefs_list_pop() to remove the last item Steven Rostedt
@ 2021-07-22 13:36 ` Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 3/4] libtracefs: Add TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS to synth Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 4/4] libtracefs: Add man pages for creating synthetic events Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2021-07-22 13:36 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	Daniel Bristot de Oliveira, Steven Rostedt (VMware)

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

Add the following APIs:

 tracefs_synth_init()
 tracefs_synth_add_match_field()
 tracefs_synth_add_compare_field()
 tracefs_synth_add_start_field()
 tracefs_synth_add_end_field()
 tracefs_synth_add_start_filter()
 tracefs_synth_add_end_filter()
 tracefs_synth_create()
 tracefs_synth_destroy()
 tracefs_synth_free()
 tracefs_synth_show()

to be able to easily create synthetic events using the histogram triggers.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h  |   66 +++
 src/tracefs-hist.c | 1079 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1145 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index 4dd77b9c4119..77a1a37f5b06 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -300,4 +300,70 @@ int tracefs_hist_continue(struct tracefs_hist *hist);
 int tracefs_hist_reset(struct tracefs_hist *hist);
 int tracefs_hist_destroy(struct tracefs_hist *hist);
 
+struct tracefs_synth;
+
+/*
+ * DELTA_END	- end_field - start_field
+ * DELTA_START	- start_field - end_field
+ * ADD		- start_field + end_field
+ */
+enum tracefs_synth_calc {
+	TRACEFS_SYNTH_DELTA_END,
+	TRACEFS_SYNTH_DELTA_START,
+	TRACEFS_SYNTH_ADD,
+};
+
+enum tracefs_synth_compare {
+	TRACEFS_COMPARE_EQ,
+	TRACEFS_COMPARE_NQ,
+	TRACEFS_COMPARE_GR,
+	TRACEFS_COMPARE_GE,
+	TRACEFS_COMPARE_LT,
+	TRACEFS_COMPARE_LE,
+	TRACEFS_COMPARE_RE,
+	TRACEFS_COMPARE_AND,
+};
+
+struct tracefs_synth *tracefs_synth_init(struct tep_handle *tep,
+					 const char *name,
+					 const char *start_system,
+					 const char *start_event,
+					 const char *end_system,
+					 const char *end_event,
+					 const char *start_match_field,
+					 const char *end_match_field,
+					 const char *match_name);
+int tracefs_synth_add_match_field(struct tracefs_synth *synth,
+				  const char *start_match_field,
+				  const char *end_match_field,
+				  const char *name);
+int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
+				    const char *start_compare_field,
+				    const char *end_compare_field,
+				    enum tracefs_synth_calc calc,
+				    const char *name);
+int tracefs_synth_add_start_field(struct tracefs_synth *synth,
+				  const char *start_field,
+				  const char *name);
+int tracefs_synth_add_end_field(struct tracefs_synth *synth,
+				const char *end_field,
+				const char *name);
+int tracefs_synth_add_start_filter(struct tracefs_synth *synth,
+				   const char *field,
+				   enum tracefs_synth_compare compare,
+				   const char *val,
+				   bool neg, bool or);
+int tracefs_synth_add_end_filter(struct tracefs_synth *synth,
+				 const char *field,
+				 enum tracefs_synth_compare compare,
+				 const char *val,
+				 bool neg, bool or);
+int tracefs_synth_create(struct tracefs_instance *instance,
+			 struct tracefs_synth *synth);
+int tracefs_synth_destroy(struct tracefs_instance *instance,
+			  struct tracefs_synth *synth);
+void tracefs_synth_free(struct tracefs_synth *synth);
+int tracefs_synth_show(struct trace_seq *seq, struct tracefs_instance *instance,
+		       struct tracefs_synth *synth);
+
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
index 9031a77eba4b..5ba865d95548 100644
--- a/src/tracefs-hist.c
+++ b/src/tracefs-hist.c
@@ -527,3 +527,1082 @@ int tracefs_hist_sort_key_direction(struct tracefs_hist *hist,
 	sort[i] = sort_key;
 	return 0;
 }
+
+/*
+ * @name: name of the synthetic event
+ * @start_system: system of the starting event
+ * @start_event: the starting event
+ * @end_system: system of the ending event
+ * @end_event: the ending event
+ * @match_names: If a match set is to be a synthetic field, it has a name
+ * @start_match: list of keys in the start event that matches end event
+ * @end_match: list of keys in the end event that matches the start event
+ * @compare_names: The synthetic field names of the compared fields
+ * @start_compare: A list of compare fields in the start to compare to end
+ * @end_compare: A list of compare fields in the end to compare to start
+ * @compare_ops: The type of operations to perform between the start and end
+ * @start_names: The fields in the start event to record
+ * @end_names: The fields in the end event to record
+ * @start_filters: The fields in the end event to record
+ * @end_filters: The fields in the end event to record
+ */
+struct tracefs_synth {
+	struct tep_handle	*tep;
+	struct tep_event	*start_event;
+	struct tep_event	*end_event;
+	char			*name;
+	char			**synthetic_fields;
+	char			**synthetic_args;
+	char			**start_keys;
+	char			**end_keys;
+	char			**start_vars;
+	char			**end_vars;
+	char			*start_filter;
+	char			*end_filter;
+
+	int			arg_cnt;
+};
+
+void tracefs_synth_free(struct tracefs_synth *synth)
+{
+	if (!synth)
+		return;
+
+	free(synth->name);
+	tracefs_list_free(synth->synthetic_fields);
+	tracefs_list_free(synth->synthetic_args);
+	tracefs_list_free(synth->start_keys);
+	tracefs_list_free(synth->end_keys);
+	tracefs_list_free(synth->start_vars);
+	tracefs_list_free(synth->end_vars);
+	free(synth->start_filter);
+	free(synth->end_filter);
+
+	tep_unref(synth->tep);
+
+	free(synth);
+}
+
+static bool verify_event_fields(struct tep_event *start_event,
+				struct tep_event *end_event,
+				const char *start_field_name,
+				const char *end_field_name,
+				struct tep_format_field **ptr_start_field)
+{
+	struct tep_format_field *start_field;
+	struct tep_format_field *end_field;
+
+	start_field = tep_find_any_field(start_event, start_field_name);
+	if (!start_field)
+		goto nodev;
+
+	if (end_event) {
+		end_field = tep_find_any_field(end_event, end_field_name);
+		if (!start_field)
+			goto nodev;
+
+		if (start_field->flags != end_field->flags ||
+		    start_field->size != end_field->size) {
+			errno = EBADE;
+			return false;
+		}
+	}
+
+	if (ptr_start_field)
+		*ptr_start_field = start_field;
+
+	return true;
+ nodev:
+	errno = ENODEV;
+	return false;
+}
+
+static char *append_string(char *str, const char *space, const char *add)
+{
+	char *new;
+	int len;
+
+	/* String must already be allocated */
+	if (!str)
+		return NULL;
+
+	len = strlen(str) + strlen(add) + 2;
+	if (space)
+		len += strlen(space);
+
+	new = realloc(str, len);
+	if (!new) {
+		free(str);
+		return NULL;
+	}
+	str = new;
+
+	if (space)
+		strcat(str, space);
+	strcat(str, add);
+
+	return str;
+}
+
+static char *add_synth_field(struct tep_format_field *field,
+			     const char *name)
+{
+	const char *type;
+	char size[64];
+	char *str;
+	bool sign;
+
+	if (field->flags & TEP_FIELD_IS_ARRAY) {
+		str = strdup("char");
+		str = append_string(str, " ", name);
+		str = append_string(str, NULL, "[");
+
+		if (!(field->flags & TEP_FIELD_IS_DYNAMIC)) {
+			snprintf(size, 64, "%d", field->size);
+			str = append_string(str, NULL, size);
+		}
+		return append_string(str, NULL, "];");
+	}
+
+	sign = field->flags & TEP_FIELD_IS_SIGNED;
+
+	switch (field->size) {
+	case 1:
+		if (!sign)
+			type = "unsigned char";
+		else
+			type = "char";
+		break;
+	case 2:
+		if (sign)
+			type = "s16";
+		else
+			type = "u16";
+		break;
+	case 4:
+		if (sign)
+			type = "s32";
+		else
+			type = "u32";
+		break;
+	case 8:
+		if (sign)
+			type = "s64";
+		else
+			type = "u64";
+		break;
+	default:
+		errno = EBADF;
+		return NULL;
+	}
+
+	str = strdup(type);
+	str = append_string(str, " ", name);
+	return append_string(str, NULL, ";");
+}
+
+static int add_var(char ***list, const char *name, const char *var, bool is_var)
+{
+	char **new;
+	char *assign;
+	int ret;
+
+	if (is_var)
+		ret = asprintf(&assign, "%s=$%s", name, var);
+	else
+		ret = asprintf(&assign, "%s=%s", name, var);
+
+	if (ret < 0)
+		return -1;
+
+	new = tracefs_list_add(*list, assign);
+	free(assign);
+
+	if (!new)
+		return -1;
+	*list = new;
+	return 0;
+}
+
+/**
+ * tracefs_synth_init - create a new tracefs_synth instance
+ * @tep: The tep handle that holds the events to work on
+ * @name: The name of the synthetic event being created
+ * @start_system: The name of the system of the start event (can be NULL)
+ * @start_event_name: The name of the start event
+ * @end_system: The name of the system of the end event (can be NULL)
+ * @end_event_name: The name of the end event
+ * @start_match_field: The name of the field in start event to match @end_match_field
+ * @end_match_field: The name of the field in end event to match @start_match_field
+ * @match_name: Name to call the fields that match (can be NULL)
+ * 
+ * Creates a tracefs_synth instance that has the minimum requirements to
+ * create a synthetic event.
+ *
+ * @name is will be the name of the synthetic event that this can create.
+ *
+ * The start event is found with @start_system and @start_event_name. If
+ * @start_system is NULL, then the first event with @start_event_name will
+ * be used.
+ *
+ * The end event is found with @end_system and @end_event_name. If
+ * @end_system is NULL, then the first event with @end_event_name will
+ * be used.
+ *
+ * The @start_match_field is the field in the start event that will be used
+ * to match the @end_match_field of the end event.
+ *
+ * If @match_name is given, then the field that matched the start and
+ * end events will be passed an a field to the sythetic event with this
+ * as the field name.
+ *
+ * Returns an allocated tracefs_synth descriptor on success and NULL
+ * on error, with the following set in errno.
+ *
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find an event or field
+ * EBADE - The start and end fields are not compatible to match 
+ * 
+ * Note, this does not modify the system. That is, the synthetic
+ * event on the system is not created. That needs to be done with
+ * tracefs_synth_create().
+ */
+struct tracefs_synth *tracefs_synth_init(struct tep_handle *tep,
+					 const char *name,
+					 const char *start_system,
+					 const char *start_event_name,
+					 const char *end_system,
+					 const char *end_event_name,
+					 const char *start_match_field,
+					 const char *end_match_field,
+					 const char *match_name)
+{
+	struct tep_event *start_event;
+	struct tep_event *end_event;
+	struct tracefs_synth *synth;
+	int ret = 0;
+
+	if (!tep || !name || !start_event_name || !end_event_name ||
+	    !start_match_field || !end_match_field) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	start_event = tep_find_event_by_name(tep, start_system,
+					     start_event_name);
+	if (!start_event) {
+		errno = ENODEV;
+		return NULL;
+	}
+
+	end_event = tep_find_event_by_name(tep, end_system,
+					   end_event_name);
+	if (!end_event) {
+		errno = ENODEV;
+		return NULL;
+	}
+
+	synth = calloc(1, sizeof(*synth));
+	if (!synth)
+		return NULL;
+
+	synth->start_event = start_event;
+	synth->end_event = end_event;
+
+	synth->name = strdup(name);
+
+	ret = tracefs_synth_add_match_field(synth, start_match_field,
+					    end_match_field, match_name);
+
+	/* Hold onto a reference to this handler */
+	tep_ref(tep);
+	synth->tep = tep;
+
+	if (!synth->name || !synth->start_keys || !synth->end_keys || ret) {
+		tracefs_synth_free(synth);
+		synth = NULL;
+	}
+
+	return synth;
+}
+
+static int add_synth_fields(struct tracefs_synth *synth,
+			    struct tep_format_field *field,
+			    const char *name)
+{
+	char **list;
+	char *str;
+	int ret;
+
+	str = add_synth_field(field, name);
+	if (!str)
+		return -1;
+
+	list = tracefs_list_add(synth->synthetic_fields, str);
+	free(str);
+	if (!list)
+		return -1;
+	synth->synthetic_fields = list;
+
+	ret = asprintf(&str, "$%s", name);
+	if (ret < 0) {
+		tracefs_list_pop(synth->synthetic_fields);
+		return -1;
+	}
+
+	list = tracefs_list_add(synth->synthetic_args, str);
+	free(str);
+	if (!list) {
+		tracefs_list_pop(synth->synthetic_fields);
+		return -1;
+	}
+
+	synth->synthetic_args = list;
+
+	return 0;
+}
+
+/**
+ * tracefs_synth_add_match_field - add another key to match events
+ * @synth: The tracefs_synth descriptor
+ * @start_match_field: The field of the start event to match the end event
+ * @end_match_field: The field of the end event to match the start event
+ * @name: The name to show in the synthetic event (NULL is allowed)
+ *
+ * This will add another set of keys to use for a match between
+ * the start event and the end event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ * EBADE - The start and end fields are not compatible to match 
+ */
+int tracefs_synth_add_match_field(struct tracefs_synth *synth,
+				  const char *start_match_field,
+				  const char *end_match_field,
+				  const char *name)
+{
+	struct tep_format_field *key_field;
+	char **list;
+	int ret;
+
+	if (!synth || !start_match_field || !end_match_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!verify_event_fields(synth->start_event, synth->end_event,
+				 start_match_field, end_match_field,
+				 &key_field))
+		return -1;
+
+	list = tracefs_list_add(synth->start_keys, start_match_field);
+	if (!list)
+		return -1;
+
+	synth->start_keys = list;
+
+	list = tracefs_list_add(synth->end_keys, end_match_field);
+	if (!list) {
+		tracefs_list_pop(synth->start_keys);
+		return -1;
+	}
+	synth->end_keys = list;
+
+	if (!name)
+		return 0;
+
+	ret = add_var(&synth->end_vars, name, end_match_field, false);
+
+	if (ret < 0)
+		goto pop_lists;
+
+	ret = add_synth_fields(synth, key_field, name);
+	if (ret < 0)
+		goto pop_lists;
+
+	return 0;
+
+ pop_lists:
+	tracefs_list_pop(synth->start_keys);
+	tracefs_list_pop(synth->end_keys);
+	return -1;
+}
+
+static char *new_arg(struct tracefs_synth *synth)
+{
+	int cnt = synth->arg_cnt + 1;
+	char *arg;
+	int ret;
+
+	ret = asprintf(&arg, "__arg__%d", cnt);
+	if (ret < 0)
+		return NULL;
+
+	synth->arg_cnt = cnt;
+	return arg;
+}
+
+/**
+ * tracefs_synth_add_compare_field - add a comparison between start and end
+ * @synth: The tracefs_synth descriptor
+ * @start_compare_field: The field of the start event to compare to the end
+ * @end_compare_field: The field of the end event to compare to the start
+ * @calc - How to go about the comparing the fields.
+ * @name: The name to show in the synthetic event (must NOT be NULL)
+ *
+ * This will add a way to compare two different fields between the
+ * start end end events.
+ *
+ * The comparing between events is decided by @calc:
+ *    TRACEFS_SYNTH_DELTA_END       - name = end - start
+ *    TRACEFS_SYNTH_DELTA_START     - name = start - end
+ *    TRACEFS_SYNTH_ADD             - name = end + start
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ * EBADE - The start and end fields are not compatible to compare
+ */
+int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
+				    const char *start_compare_field,
+				    const char *end_compare_field,
+				    enum tracefs_synth_calc calc,
+				    const char *name)
+{
+	struct tep_format_field *start_field;
+	char *start_arg;
+	char *compare;
+	int ret;
+
+	/* Compare fields require a name */
+	if (!name || !start_compare_field || !end_compare_field) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	if (!verify_event_fields(synth->start_event, synth->end_event,
+				 start_compare_field, end_compare_field,
+				 &start_field))
+		return -1;
+
+	/* Calculations are not allowed on string */
+	if (start_field->flags & (TEP_FIELD_IS_ARRAY |
+				  TEP_FIELD_IS_DYNAMIC)) {
+		errno = -EINVAL;
+		return -1;
+	}
+
+	start_arg = new_arg(synth);
+	if (!start_arg)
+		return -1;
+
+	ret = add_var(&synth->start_vars, start_arg, start_compare_field, false);
+	if (ret < 0) {
+		free(start_arg);
+		return -1;
+	}
+
+	ret = -1;
+	switch (calc) {
+	case TRACEFS_SYNTH_DELTA_END:
+		ret = asprintf(&compare, "%s-$%s", end_compare_field,
+			       start_arg);
+		break;
+	case TRACEFS_SYNTH_DELTA_START:
+		ret = asprintf(&compare, "$%s-%s", start_arg,
+			       end_compare_field);
+		break;
+	case TRACEFS_SYNTH_ADD:
+		ret = asprintf(&compare, "%s+$%s", end_compare_field,
+			       start_arg);
+		break;
+	}
+	free(start_arg);
+	if (ret < 0)
+		return -1;
+
+	ret = add_var(&synth->end_vars, name, compare, false);
+	if (ret < 0)
+		goto out_free;
+
+	ret = add_synth_fields(synth, start_field, name);
+	if (ret < 0)
+		goto out_free;
+
+ out_free:
+	free(compare);
+
+	return ret ? -1 : 0;
+}
+
+/**
+ * tracefs_synth_add_start_field - add a start field to save
+ * @synth: The tracefs_synth descriptor
+ * @start_field: The field of the start event to save
+ * @name: The name to show in the synthetic event (if NULL @start_field is used)
+ *
+ * This adds a field named by @start_field of the start event to
+ * record in the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ */
+int tracefs_synth_add_start_field(struct tracefs_synth *synth,
+				  const char *start_field,
+				  const char *name)
+{
+	struct tep_format_field *field;
+	char *start_arg;
+	int ret;
+
+	if (!synth || !start_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!name)
+		name = start_field;
+
+	if (!verify_event_fields(synth->start_event, NULL,
+				 start_field, NULL, &field))
+		return -1;
+
+	start_arg = new_arg(synth);
+	if (!start_arg)
+		return -1;
+
+	ret = add_var(&synth->start_vars, start_arg, start_field, false);
+	if (ret)
+		goto out_free;
+
+	ret = add_var(&synth->end_vars, name, start_arg, true);
+	if (ret)
+		goto out_free;
+
+	ret = add_synth_fields(synth, field, name);
+
+ out_free:
+	free(start_arg);
+	return ret;
+}
+
+/**
+ * tracefs_synth_add_end_field - add a end field to save
+ * @synth: The tracefs_synth descriptor
+ * @end_field: The field of the end event to save
+ * @name: The name to show in the synthetic event (if NULL @end_field is used)
+ *
+ * This adds a field named by @end_field of the start event to
+ * record in the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be
+ * ENODEV - could not find a field
+ */
+int tracefs_synth_add_end_field(struct tracefs_synth *synth,
+				const char *end_field,
+				const char *name)
+{
+	struct tep_format_field *field;
+	int ret;
+
+	if (!synth || !end_field) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!name)
+		name = end_field;
+
+	if (!verify_event_fields(synth->end_event, NULL,
+				 end_field, NULL, &field))
+		return -1;
+
+	ret = add_var(&synth->end_vars, name, end_field, false);
+	if (ret)
+		goto out;
+
+	ret = add_synth_fields(synth, field, name);
+
+ out:
+	return ret;
+}
+
+static int add_synth_filter(char **filter, const char *field,
+			    enum tracefs_synth_compare compare,
+			    const char *val, bool is_string,
+			    bool neg, bool or)
+{
+	const char *minus = "";
+	const char *op;
+	char *str = NULL;
+	int ret;
+
+	switch (compare) {
+	case TRACEFS_COMPARE_EQ:
+		op = "==";
+		break;
+
+	case TRACEFS_COMPARE_NQ:
+		op = "!=";
+		break;
+
+	case TRACEFS_COMPARE_GR:
+		op = ">";
+		if (is_string)
+			goto inval;
+		break;
+
+	case TRACEFS_COMPARE_GE:
+		op = ">=";
+		if (is_string)
+			goto inval;
+		break;
+
+	case TRACEFS_COMPARE_LT:
+		op = "<";
+		if (is_string)
+			goto inval;
+		break;
+
+	case TRACEFS_COMPARE_LE:
+		op = "<=";
+		if (is_string)
+			goto inval;
+		break;
+
+	case TRACEFS_COMPARE_RE:
+		op = "~";
+		if (!is_string)
+			goto inval;
+		break;
+
+	case TRACEFS_COMPARE_AND:
+		op = "&";
+		if (is_string)
+			goto inval;
+		break;
+	}
+
+	if (neg)
+		minus = "-";
+
+	if (is_string && val[0] != '"')
+		ret = asprintf(&str, "%s(%s %s \"%s\")",
+			       minus, field, op, val);
+	else
+		ret = asprintf(&str, "%s(%s %s %s)",
+			       minus, field, op, val);
+
+	if (ret < 0)
+		return -1;
+
+	if (*filter) {
+		char *new;
+		char *conjunction = or ? "||" : "&&";
+
+		ret = asprintf(&new, "%s %s %s", *filter,
+			       conjunction, str);
+		free(str);
+		if (ret < 0)
+			return -1;
+		free(*filter);
+		*filter = new;
+	} else {
+		*filter = str;
+	}
+
+	return 0;
+inval:
+	errno = -EINVAL;
+	return -1;
+}
+
+int tracefs_synth_add_start_filter(struct tracefs_synth *synth,
+				   const char *field,
+				   enum tracefs_synth_compare compare,
+				   const char *val,
+				   bool neg, bool or)
+{
+	struct tep_format_field *start_field;
+	bool is_string;
+
+	if (!field || !val)
+		goto inval;
+
+	if (!verify_event_fields(synth->start_event, NULL,
+				 field, NULL, &start_field))
+		return -1;
+
+	is_string = start_field->flags & TEP_FIELD_IS_STRING;
+
+	if (!is_string && (start_field->flags & TEP_FIELD_IS_ARRAY))
+		goto inval;
+
+	return add_synth_filter(&synth->start_filter,
+				field, compare, val, is_string,
+				neg, or);
+inval:
+	errno = -EINVAL;
+	return -1;
+}
+
+int tracefs_synth_add_end_filter(struct tracefs_synth *synth,
+				 const char *field,
+				 enum tracefs_synth_compare compare,
+				 const char *val,
+				 bool neg, bool or)
+{
+	struct tep_format_field *end_field;
+	bool is_string;
+
+	if (!field || !val)
+		goto inval;
+
+	if (!verify_event_fields(synth->end_event, NULL,
+				 field, NULL, &end_field))
+		return -1;
+
+	is_string = end_field->flags & TEP_FIELD_IS_STRING;
+
+	if (!is_string && (end_field->flags & TEP_FIELD_IS_ARRAY))
+		goto inval;
+
+	return add_synth_filter(&synth->end_filter,
+				field, compare, val, is_string,
+				neg, or);
+inval:
+	errno = -EINVAL;
+	return -1;
+}
+
+static char *create_synthetic_event(struct tracefs_synth *synth)
+{
+	char *synthetic_event;
+	const char *field;
+	int i;
+
+	synthetic_event = strdup(synth->name);
+	if (!synthetic_event)
+		return NULL;
+
+	for (i = 0; synth->synthetic_fields && synth->synthetic_fields[i]; i++) {
+		field = synth->synthetic_fields[i];
+		synthetic_event = append_string(synthetic_event, " ", field);
+	}
+
+	return synthetic_event;
+}
+
+static int remove_synthetic(const char *synthetic)
+{
+	char *str;
+	int ret;
+
+	ret = asprintf(&str, "!%s", synthetic);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, "synthetic_events", str);
+	free(str);
+	return ret < 0 ? -1 : 0;
+}
+
+static int remove_hist(struct tracefs_instance *instance,
+		       struct tep_event *event, const char *hist)
+{
+	char *str;
+	int ret;
+
+	ret = asprintf(&str, "!%s", hist);
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_event_file_append(instance, event->system, event->name,
+				  "trigger", str);
+	free(str);
+	return ret < 0 ? -1 : 0;
+}
+
+static char *create_hist(char **keys, char **vars)
+{
+	char *hist = strdup("hist:keys=");
+	char *name;
+	int i;
+
+	if (!hist)
+		return NULL;
+
+	for (i = 0; keys[i]; i++) {
+		name = keys[i];
+		if (i)
+			hist = append_string(hist, NULL, ",");
+		hist = append_string(hist, NULL, name);
+	}
+
+	if (!vars)
+		return hist;
+
+	hist = append_string(hist, NULL, ":");
+
+	for (i = 0; vars[i]; i++) {
+		name = vars[i];
+		if (i)
+			hist = append_string(hist, NULL, ",");
+		hist = append_string(hist, NULL, name);
+	}
+
+	return hist;
+}
+
+static char *create_end_hist(struct tracefs_synth *synth)
+{
+	const char *name;
+	char *end_hist;
+	int i;
+
+	end_hist = create_hist(synth->end_keys, synth->end_vars);
+	end_hist = append_string(end_hist, NULL, ":onmatch(");
+	end_hist = append_string(end_hist, NULL, synth->start_event->system);
+	end_hist = append_string(end_hist, NULL, ".");
+	end_hist = append_string(end_hist, NULL, synth->start_event->name);
+	end_hist = append_string(end_hist, NULL, ").trace(");
+	end_hist = append_string(end_hist, NULL, synth->name);
+
+	for (i = 0; synth->synthetic_args && synth->synthetic_args[i]; i++) {
+		name = synth->synthetic_args[i];
+
+		end_hist = append_string(end_hist, NULL, ",");
+		end_hist = append_string(end_hist, NULL, name);
+	}
+
+	return append_string(end_hist, NULL, ")");
+}
+
+/**
+ * tracefs_synth_create - creates the synthetic event on the system
+ * @instance: The instance to modify the start and end events
+ * @synth: The tracefs_synth descriptor
+ *
+ * This creates the synthetic events. The @instance is used for writing
+ * the triggers into the start and end events.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be or a problem
+ *   writing into the system.
+ */
+int tracefs_synth_create(struct tracefs_instance *instance,
+			 struct tracefs_synth *synth)
+{
+	char *synthetic_event;
+	char *start_hist = NULL;
+	char *end_hist = NULL;
+	int ret;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	synthetic_event = create_synthetic_event(synth);
+	if (!synthetic_event)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, "synthetic_events",
+					   synthetic_event);
+	if (ret < 0)
+		goto free_synthetic;
+
+	start_hist = create_hist(synth->start_keys, synth->start_vars);
+	if (synth->start_filter) {
+		start_hist = append_string(start_hist, NULL, " if ");
+		start_hist = append_string(start_hist, NULL, synth->start_filter);
+	}
+	if (!start_hist)
+		goto remove_synthetic;
+
+	end_hist = create_end_hist(synth);
+	if (synth->end_filter) {
+		end_hist = append_string(end_hist, NULL, " if ");
+		end_hist = append_string(end_hist, NULL, synth->end_filter);
+	}
+	if (!end_hist)
+		goto remove_synthetic;
+
+	ret = tracefs_event_file_append(instance, synth->start_event->system,
+					synth->start_event->name,
+					"trigger", start_hist);
+	if (ret < 0)
+		goto remove_synthetic;
+
+	ret = tracefs_event_file_append(instance, synth->end_event->system,
+					synth->end_event->name,
+					"trigger", end_hist);
+	if (ret < 0)
+		goto remove_start_hist;
+
+	free(start_hist);
+	free(end_hist);
+
+	return 0;
+
+ remove_start_hist:
+	remove_hist(instance, synth->start_event, start_hist);
+ remove_synthetic:
+	free(end_hist);
+	free(start_hist);
+	remove_synthetic(synthetic_event);
+ free_synthetic:
+	free(synthetic_event);
+	return -1;
+}
+
+/**
+ * tracefs_synth_destroy - delete the synthetic event from the system
+ * @instance: The instance to modify the start and end events
+ * @synth: The tracefs_synth descriptor
+ *
+ * This will destroy a synthetic event created by tracefs_synth_create()
+ * with the same @instance and @synth.
+ *
+ * It will attempt to disable the synthetic event, but if other instances
+ * have it active, it is likely to fail, which will likely fail on
+ * all other parts of tearing down the synthetic event.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ * ENIVAL - a parameter is passed as NULL that should not be or a problem
+ *   writing into the system.
+ */
+int tracefs_synth_destroy(struct tracefs_instance *instance,
+			  struct tracefs_synth *synth)
+{
+	char *synthetic_event;
+	char *hist;
+	int ret;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* Try to disable the event if possible */
+	tracefs_event_disable(instance, "synthetic", synth->name);
+
+	hist = create_end_hist(synth);
+	if (synth->end_filter) {
+		hist = append_string(hist, NULL, " if ");
+		hist = append_string(hist, NULL, synth->end_filter);
+	}
+	if (!hist)
+		return -1;
+	ret = remove_hist(instance, synth->end_event, hist);
+	free(hist);
+
+	hist = create_hist(synth->start_keys, synth->start_vars);
+	if (synth->start_filter) {
+		hist = append_string(hist, NULL, " if ");
+		hist = append_string(hist, NULL, synth->start_filter);
+	}
+	if (!hist)
+		return -1;
+
+	ret = remove_hist(instance, synth->start_event, hist);
+	free(hist);
+
+	synthetic_event = create_synthetic_event(synth);
+	if (!synthetic_event)
+		return -1;
+
+	ret = remove_synthetic(synthetic_event);
+
+	return ret ? -1 : 0;
+}
+
+/**
+ * tracefs_synth_show - show the command lines to create the synthetic event
+ * @seq: The trace_seq to store the command lines in
+ * @instance: The instance to modify the start and end events
+ * @synth: The tracefs_synth descriptor
+ *
+ * This will list the "echo" commands that are equivalent to what would
+ * be executed by the tracefs_synth_create() command.
+ *
+ * Returns 0 on succes and -1 on error.
+ * On error, errno is set to:
+ * ENOMEM - memory allocation failure.
+ */
+int tracefs_synth_show(struct trace_seq *seq,
+		       struct tracefs_instance *instance,
+		       struct tracefs_synth *synth)
+{
+	char *synthetic_event = NULL;
+	char *hist = NULL;
+	char *path;
+	int ret = -1;
+
+	if (!synth) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	synthetic_event = create_synthetic_event(synth);
+	if (!synthetic_event)
+		return -1;
+
+	path = trace_find_tracing_dir();
+	if (!path)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s' > %s/synthetic_events\n",
+			 synthetic_event, path);
+
+	tracefs_put_tracing_file(path);
+	path = tracefs_instance_get_dir(instance);
+
+	hist = create_hist(synth->start_keys, synth->start_vars);
+	if (synth->start_filter) {
+		hist = append_string(hist, NULL, " if ");
+		hist = append_string(hist, NULL, synth->start_filter);
+	}
+	if (!hist)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s' > %s/events/%s/%s/trigger\n",
+			 hist, path, synth->start_event->system,
+			 synth->start_event->name);
+	free(hist);
+	hist = create_end_hist(synth);
+
+	if (synth->end_filter) {
+		hist = append_string(hist, NULL, " if ");
+		hist = append_string(hist, NULL, synth->end_filter);
+	}
+	if (!hist)
+		goto out_free;
+
+	trace_seq_printf(seq, "echo '%s' > %s/events/%s/%s/trigger\n",
+			 hist, path, synth->end_event->system,
+			 synth->end_event->name);
+
+	ret = 0;
+ out_free:
+	free(synthetic_event);
+	free(hist);
+	tracefs_put_tracing_file(path);
+	return ret;
+}
-- 
2.30.2


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

* [PATCH v2 3/4] libtracefs: Add TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS to synth
  2021-07-22 13:36 [PATCH v2 0/4] libtracefs: Creating synthetic events Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 1/4] libtracefs: Add tracefs_list_pop() to remove the last item Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 2/4] libtracefs: Create a way to create a synthetic event Steven Rostedt
@ 2021-07-22 13:36 ` Steven Rostedt
  2021-07-22 13:36 ` [PATCH v2 4/4] libtracefs: Add man pages for creating synthetic events Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2021-07-22 13:36 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	Daniel Bristot de Oliveira, Steven Rostedt (VMware)

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

Add the fields of TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS that can
be used to calculate the timestamps between the events for the synthetic
event creation.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h  |  3 +++
 src/tracefs-hist.c | 50 ++++++++++++++++++++++++++++++++++------------
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/include/tracefs.h b/include/tracefs.h
index 77a1a37f5b06..9cfd2577da2e 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -324,6 +324,9 @@ enum tracefs_synth_compare {
 	TRACEFS_COMPARE_AND,
 };
 
+#define TRACEFS_TIMESTAMP "common_timestamp"
+#define TRACEFS_TIMESTAMP_USECS "common_timestamp.usecs"
+
 struct tracefs_synth *tracefs_synth_init(struct tep_handle *tep,
 					 const char *name,
 					 const char *start_system,
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
index 5ba865d95548..8b9078791ab2 100644
--- a/src/tracefs-hist.c
+++ b/src/tracefs-hist.c
@@ -563,6 +563,18 @@ struct tracefs_synth {
 	int			arg_cnt;
 };
 
+static const struct tep_format_field common_timestamp = {
+	.type			= "u64",
+	.name			= "common_timestamp",
+	.size			= 8,
+};
+
+static const struct tep_format_field common_timestamp_usecs = {
+	.type			= "u64",
+	.name			= "common_timestamp.usecs",
+	.size			= 8,
+};
+
 void tracefs_synth_free(struct tracefs_synth *synth)
 {
 	if (!synth)
@@ -583,21 +595,33 @@ void tracefs_synth_free(struct tracefs_synth *synth)
 	free(synth);
 }
 
+static const struct tep_format_field *get_event_field(struct tep_event *event,
+					 const char *field_name)
+{
+	if (!strcmp(field_name, TRACEFS_TIMESTAMP))
+		return &common_timestamp;
+
+	if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
+		return &common_timestamp_usecs;
+
+	return tep_find_any_field(event, field_name);
+}
+
 static bool verify_event_fields(struct tep_event *start_event,
 				struct tep_event *end_event,
 				const char *start_field_name,
 				const char *end_field_name,
-				struct tep_format_field **ptr_start_field)
+				const struct tep_format_field **ptr_start_field)
 {
-	struct tep_format_field *start_field;
-	struct tep_format_field *end_field;
+	const struct tep_format_field *start_field;
+	const struct tep_format_field *end_field;
 
-	start_field = tep_find_any_field(start_event, start_field_name);
+	start_field = get_event_field(start_event, start_field_name);
 	if (!start_field)
 		goto nodev;
 
 	if (end_event) {
-		end_field = tep_find_any_field(end_event, end_field_name);
+		end_field = get_event_field(end_event, end_field_name);
 		if (!start_field)
 			goto nodev;
 
@@ -644,7 +668,7 @@ static char *append_string(char *str, const char *space, const char *add)
 	return str;
 }
 
-static char *add_synth_field(struct tep_format_field *field,
+static char *add_synth_field(const struct tep_format_field *field,
 			     const char *name)
 {
 	const char *type;
@@ -828,7 +852,7 @@ struct tracefs_synth *tracefs_synth_init(struct tep_handle *tep,
 }
 
 static int add_synth_fields(struct tracefs_synth *synth,
-			    struct tep_format_field *field,
+			    const struct tep_format_field *field,
 			    const char *name)
 {
 	char **list;
@@ -885,7 +909,7 @@ int tracefs_synth_add_match_field(struct tracefs_synth *synth,
 				  const char *end_match_field,
 				  const char *name)
 {
-	struct tep_format_field *key_field;
+	const struct tep_format_field *key_field;
 	char **list;
 	int ret;
 
@@ -975,7 +999,7 @@ int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
 				    enum tracefs_synth_calc calc,
 				    const char *name)
 {
-	struct tep_format_field *start_field;
+	const struct tep_format_field *start_field;
 	char *start_arg;
 	char *compare;
 	int ret;
@@ -1060,7 +1084,7 @@ int tracefs_synth_add_start_field(struct tracefs_synth *synth,
 				  const char *start_field,
 				  const char *name)
 {
-	struct tep_format_field *field;
+	const struct tep_format_field *field;
 	char *start_arg;
 	int ret;
 
@@ -1114,7 +1138,7 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
 				const char *end_field,
 				const char *name)
 {
-	struct tep_format_field *field;
+	const struct tep_format_field *field;
 	int ret;
 
 	if (!synth || !end_field) {
@@ -1235,7 +1259,7 @@ int tracefs_synth_add_start_filter(struct tracefs_synth *synth,
 				   const char *val,
 				   bool neg, bool or)
 {
-	struct tep_format_field *start_field;
+	const struct tep_format_field *start_field;
 	bool is_string;
 
 	if (!field || !val)
@@ -1264,7 +1288,7 @@ int tracefs_synth_add_end_filter(struct tracefs_synth *synth,
 				 const char *val,
 				 bool neg, bool or)
 {
-	struct tep_format_field *end_field;
+	const struct tep_format_field *end_field;
 	bool is_string;
 
 	if (!field || !val)
-- 
2.30.2


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

* [PATCH v2 4/4] libtracefs: Add man pages for creating synthetic events
  2021-07-22 13:36 [PATCH v2 0/4] libtracefs: Creating synthetic events Steven Rostedt
                   ` (2 preceding siblings ...)
  2021-07-22 13:36 ` [PATCH v2 3/4] libtracefs: Add TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS to synth Steven Rostedt
@ 2021-07-22 13:36 ` Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2021-07-22 13:36 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Tom Zanussi, Masami Hiramatsu, Namhyung Kim,
	Daniel Bristot de Oliveira, Steven Rostedt (VMware)

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

Add the man pages that describe all the synthetic event operations.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 Documentation/libtracefs-synth.txt | 353 +++++++++++++++++++++++++++++
 1 file changed, 353 insertions(+)
 create mode 100644 Documentation/libtracefs-synth.txt

diff --git a/Documentation/libtracefs-synth.txt b/Documentation/libtracefs-synth.txt
new file mode 100644
index 000000000000..7497dc16756c
--- /dev/null
+++ b/Documentation/libtracefs-synth.txt
@@ -0,0 +1,353 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_synth_init, tracefs_synth_add_match_field, tracefs_synth_add_compare_field, tracefs_synth_add_start_field,
+tracefs_synth_add_end_field, tracefs_synth_add_start_filter, tracefs_synth_add_end_filter, tracefs_synth_create,
+tracefs_synth_destroy, tracefs_synth_free, tracefs_synth_show - Creation of synthetic events
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_synth pass:[*]tracefs_synth_init(struct tep_handle pass:[*]tep,
+					 const char pass:[*]name,
+					 const char pass:[*]start_system,
+					 const char pass:[*]start_event,
+					 const char pass:[*]end_system,
+					 const char pass:[*]end_event,
+					 const char pass:[*]start_match_field,
+					 const char pass:[*]end_match_field,
+					 const char pass:[*]match_name);
+int tracefs_synth_add_match_field(struct tracefs_synth pass:[*]synth,
+				  const char pass:[*]start_match_field,
+				  const char pass:[*]end_match_field,
+				  const char pass:[*]name);
+int tracefs_synth_add_compare_field(struct tracefs_synth pass:[*]synth,
+				    const char pass:[*]start_compare_field,
+				    const char pass:[*]end_compare_field,
+				    enum tracefs_synth_calc calc,
+				    const char pass:[*]name);
+int tracefs_synth_add_start_field(struct tracefs_synth pass:[*]synth,
+				  const char pass:[*]start_field,
+				  const char pass:[*]name);
+int tracefs_synth_add_end_field(struct tracefs_synth pass:[*]synth,
+				const char pass:[*]end_field,
+				const char pass:[*]name);
+int tracefs_synth_add_start_filter(struct tracefs_synth pass:[*]synth,
+				   const char pass:[*]field,
+				   enum tracefs_synth_compare compare,
+				   const char pass:[*]val,
+				   bool neg, bool or);
+int tracefs_synth_add_end_filter(struct tracefs_synth pass:[*]synth,
+				 const char pass:[*]field,
+				 enum tracefs_synth_compare compare,
+				 const char pass:[*]val,
+				 bool neg, bool or);
+int tracefs_synth_create(struct tracefs_instance pass:[*]instance,
+			 struct tracefs_synth pass:[*]synth);
+int tracefs_synth_destroy(struct tracefs_instance pass:[*]instance,
+			  struct tracefs_synth pass:[*]synth);
+void tracefs_synth_free(struct tracefs_synth pass:[*]synth);
+int tracefs_synth_show(struct trace_seq pass:[*]seq, struct tracefs_instance pass:[*]instance,
+		       struct tracefs_synth pass:[*]synth);
+
+--
+
+DESCRIPTION
+-----------
+Synthetic events are dynamic events that are created by matching
+two other events which triggers a synthetic event. One event is the starting
+event which some field is recorded, and when the second event is executed,
+if it has a field (or fields) that matches the starting event's field (or fields)
+then it will trigger the synthetic event. The field values other than the matching
+fields may be passed from the starting event to the end event to perform calculations
+on, or to simply pass as a parameter to the synthetic event.
+
+One common use case is to set "sched_waking" as the starting event. This event is
+triggered when a process is awoken. Then set "sched_switch" as the ending event.
+This event is triggered when a new task is scheduled on the CPU. By setting
+the "common_pid" of both events as the matching fields, the time between the
+two events is considered the wake up latency of that process. Use *TRACEFS_TIMESTAMP*
+as a field for both events to calculate the delta in nanoseconds, or use
+*TRACEFS_TIMESTAMP_USECS" as the compare fields for both events to calculate the
+delta in microseconds. This is used as the example below.
+
+*tracefs_synth_init*() allocates and initializes a synthetic event.
+It does not create the synthetic event, but supplies the minimal information
+to do so. See *tracefs_synth_create*() below for how to create the synthetic
+event in the system. It requires a _tep_ handler that can be created by
+*tracefs_local_events*(3) for more information. The _name_ holds the name
+of the synthetic event that will be created. The _start_system is the name
+of the system for the starting event. It may be NULL and the first event
+with the name of _start_event_ will be chosen. The _end_system_ is the
+name of the system for theh ending event. It may be NULL and the first event
+with the name of _end_event_ will be chosen as the ending event. If _match_name_
+is given, then this will be the field of the created synthetic event that
+holds the matching keys of the starting event's _start_match_field_ and
+the ending event's _end_match_field_. If _match_name_ is NULL, then it will
+not be recorded in the created synthetic event.
+
+*tracefs_synth_add_match_field*() will add a second key to match between the
+starting event and the ending event. If _name_ is given, then the content
+of the matching field will be saved by this _name_ in the synthetic event.
+The _start_match_field_ is the field of the starting event to mach with the
+ending event's _end_match_field.
+
+*tracefs_synth_add_compare_field*() is used to compare the _start_compare_field_
+of the starting event with the _end_compare_field_ of the ending event. The _name_
+must be given so that the result will be saved by the synthetic event. It makes
+no sense to not pass this to the synthetic event after doing the work of
+the compared fields, as it serves no other purpose. The _calc_ parameter
+can be one of:
+
+*TRACEFS_SYNTH_DELTA_END* - calculate the difference between the content in
+ the _end_compare_field_ from the content of the _start_compare_field_.
+
+ _name_ = _end_compare_field_ - _start_compare_field_
+
+*TRACEFS_SYNTH_DELTA_START* - calculate the difference between the content in
+ the _start_compare_field_ from the content of the _end_compare_field_.
+
+ _name_ = _start_compare_field_ - _end_compare_field_
+
+*TRACEFS_SYNTH_ADD* - Add the content of the _start_compare_field_ to the
+  content of the _end_compare_field_.
+
+ _name_ = _start_compare_field_ + _end_compare_field_
+
+*tracefs_synth_add_start_field*() - Records the _start_field_ of the starting
+event as _name_ in the synthetic event. If _name_ is NULL, then the name used
+will be the same as _start_field_.
+
+*tracefs_synth_add_end_field*() - Records the _end_field_ of the ending
+event as _name_ in the synthetic event. If _name_ is NULL, then the name used
+will be the same as _end_field_.
+
+*tracefs_synth_add_start_filter*() adds a filter to the starting event
+comparing the content of the starting event's _field_ to _val_ based
+on _compare_. If _neg_ is set, then the compare is wrapped in parenthesis
+and negated. The _or_ field only is used if more than one call to
+*tracefs_synth_add_start_filter*() is done, and if _or_ is set, the
+next compare is "or'd" (||), otherwise it is "and'd" (&&). _compare_
+may be one of:
+
+*TRACEFS_COMPARE_EQ* - _field_ == _val_
+
+*TRACEFS_COMPARE_NQ* - _field_ != _val_
+
+*TRACEFS_COMPARE_GR* - _field_ > _val_
+
+*TRACEFS_COMPARE_GE* - _field_ >= _val_
+
+*TRACEFS_COMPARE_LT* - _field_ < _val_
+
+*TRACEFS_COMPARE_LE* - _field_ <pass:[=] _val_
+
+*TRACEFS_COMPARE_RE* - _field_ ~ "_val_" : where _field_ is a string.
+
+*TRACEFS_COMPARE_AND* - _field_ & _val_ : where _field_ is a flags field.
+
+*tracefs_synth_add_end_filter*() is the same as *tracefs_synth_add_start_filter* but
+filters on the ending event.
+
+*tracefs_synth_create*() creates the synthetic event in the system in the system
+in the _instance_ provided. Note, synthetic events apply across all instances,
+but some creation requires histograms to be established, which are local to
+instances.
+
+*tracefs_synth_destroy*() destroys the synthetic event. It will attempt to stop
+the running of it in the given _instance_, but if its running in another instance
+this may fail as busy.
+
+*tracefs_synth_free*() frees the allocated descriptor returned by
+*tracefs_synth_init*().
+
+*tracefs_synth_show*() acts like *tracefs_synth_create*(), but instead of creating
+the synthetic event in the given _instance_, it will write the echo commands to
+manually create it in the _seq_ given.
+
+RETURN VALUE
+------------
+*tracefs_synth_init*() returns an allocated struct tracefs_synth descriptor
+on sucess or NULL on error.
+
+All other functions that return an integer returns zero on success or -1
+on error.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user when required.
+
+*EINVAL* Either a parameter is not valid (NULL when it should not be)
+  or a field that is not compatible for calculations.
+
+*ENODEV* An event or one of its fields is not found.
+
+*EBADE* The fields of the start and end events are not compatible for
+  either matching or comparing.
+
+*ENOMEM* not enough memory is available.
+
+And more errors may have happened from the system calls to the system.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs.h>
+
+#define start_event "sched_waking"
+#define start_field "pid"
+
+#define end_event "sched_switch"
+#define end_field "next_pid"
+
+#define match_name "pid"
+
+static struct tracefs_synth *synth;
+
+static void make_event(void)
+{
+	struct tep_handle *tep;
+
+	/* Load all events from the system */
+	tep = tracefs_local_events(NULL);
+	
+	/* Initialize the synthetic event */
+	synth = tracefs_synth_init(tep, "wakeup_lat",
+				   NULL, start_event,
+				   NULL, end_event,
+				   start_field, end_field,
+				   match_name);
+
+	/* The tep is no longer needed */
+	tep_free(tep);
+
+
+	/* Save the "prio" field as "prio" from the start event */
+	tracefs_synth_add_start_field(synth, "prio", NULL);
+
+	/* Save the "next_comm" as "comm" from the end event */
+	tracefs_synth_add_end_field(synth, "next_comm", "comm");
+
+	/* Save the "prev_prio" as "prev_prio" from the end event */
+	tracefs_synth_add_end_field(synth, "prev_prio", NULL);
+
+	/*
+	 * Take a microsecond time difference between end and start
+	 * and record as "delta"
+	 */
+	tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_TIMESTAMP_USECS,
+					TRACEFS_SYNTH_DELTA_END, "delta");
+
+	/* Only record if start event "prio" is less than 100 */
+	tracefs_synth_add_start_filter(synth, "prio",
+				       TRACEFS_COMPARE_LT, "100",
+				       false, false);
+
+	/*
+	 * Only record if end event "next_prio" is less than 50
+	 * or the previous task's prio was less than 100.
+	 */
+	tracefs_synth_add_end_filter(synth, "next_prio",
+				       TRACEFS_COMPARE_LT, "50",
+				       false, false);
+	tracefs_synth_add_end_filter(synth, "prev_prio",
+				       TRACEFS_COMPARE_LT, "100",
+				       false, true);
+}
+
+/* Display how to create the synthetic event */
+static void show_event(void)
+{
+	struct trace_seq s;
+
+	trace_seq_init(&s);
+
+	tracefs_synth_show(&s, NULL, synth);
+	trace_seq_terminate(&s);
+	trace_seq_do_printf(&s);
+	trace_seq_destroy(&s);
+}
+
+int main (int argc, char **argv)
+{
+	make_event();
+
+	if (argc > 1) {
+		if (!strcmp(argv[1], "create")) {
+			/* Create the synthetic event */
+			tracefs_synth_create(NULL, synth);
+		} else if (!strcmp(argv[1], "delete")) {
+			/* Delete the synthetic event */
+			tracefs_synth_destroy(NULL, synth);
+		} else {
+			printf("usage: %s [create|delete]\n", argv[0]);
+			exit(-1);
+		}
+	} else
+		show_event();
+
+	tracefs_synth_free(synth);
+
+	return 0;
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_,
+_tracefs_hist_alloc(3)_,
+_tracefs_hist_free(3)_,
+_tracefs_hist_add_key(3)_,
+_tracefs_hist_add_value(3)_,
+_tracefs_hist_add_name(3)_,
+_tracefs_hist_start(3)_,
+_tracefs_hist_destory(3)_,
+_tracefs_hist_add_sort_key(3)_,
+_tracefs_hist_sort_key_direction(3)_
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
-- 
2.30.2


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

end of thread, back to index

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-22 13:36 [PATCH v2 0/4] libtracefs: Creating synthetic events Steven Rostedt
2021-07-22 13:36 ` [PATCH v2 1/4] libtracefs: Add tracefs_list_pop() to remove the last item Steven Rostedt
2021-07-22 13:36 ` [PATCH v2 2/4] libtracefs: Create a way to create a synthetic event Steven Rostedt
2021-07-22 13:36 ` [PATCH v2 3/4] libtracefs: Add TRACEFS_TIMESTAMP and TRACEFS_TIMESTAMP_USECS to synth Steven Rostedt
2021-07-22 13:36 ` [PATCH v2 4/4] libtracefs: Add man pages for creating synthetic events Steven Rostedt

Linux-Trace-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-trace-devel/0 linux-trace-devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-trace-devel linux-trace-devel/ https://lore.kernel.org/linux-trace-devel \
		linux-trace-devel@vger.kernel.org
	public-inbox-index linux-trace-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-trace-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git