diff mbox series

[v3,3/4] libtracefs: Add tracefs_follow_event() API

Message ID 20221115040838.2456632-4-rostedt@goodmis.org (mailing list archive)
State Accepted
Commit 8da05d9697ab09f7b615c58ef03ffd3898e9a2da
Headers show
Series libtracefs: Add more iterator helpers | expand

Commit Message

Steven Rostedt Nov. 15, 2022, 4:08 a.m. UTC
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Add tracefs_follow_event() API that allows to only receive a callback for
a specific event.

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 Documentation/libtracefs-iterator.txt | 129 ++++++++++++++++++--------
 Documentation/libtracefs.txt          |   6 ++
 include/tracefs-local.h               |  10 ++
 include/tracefs.h                     |   6 ++
 src/tracefs-events.c                  | 101 +++++++++++++++++++-
 5 files changed, 209 insertions(+), 43 deletions(-)
diff mbox series

Patch

diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt
index a291f8f169ba..f53991f5cebe 100644
--- a/Documentation/libtracefs-iterator.txt
+++ b/Documentation/libtracefs-iterator.txt
@@ -3,7 +3,7 @@  libtracefs(3)
 
 NAME
 ----
-tracefs_iterate_raw_events, tracefs_iterate_stop - Iterate over events in the ring buffer
+tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event - Iterate over events in the ring buffer
 
 SYNOPSIS
 --------
@@ -17,6 +17,12 @@  int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs
 				 void pass:[*]_callback_context_);
 void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
 
+int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+			  const char pass:[*]_system_, const char pass:[*]_event_name_,
+			  int (pass:[*]_callback_)(struct tep_event pass:[*],
+					  struct tep_record pass:[*],
+					  int, void pass:[*]),
+			  void pass:[*]_callback_data_):
 --
 
 DESCRIPTION
@@ -43,6 +49,14 @@  to halt. This can be called from either a callback that is called by
 the iterator (even though a return of non-zero will stop it), or from another
 thread.
 
+The *tracefs_follow_event()* is used with *tracefs_iterate_raw_events()* but
+intead of the callback being called for every event, it is only called for the
+specified _system_ / _event_name_ given to the function. The _callback_ is the
+same as for *tracefs_iterate_raw_events()*, and the passed in _callback_context_
+will be passed to the _callback_ as well. Note, if it returns something other
+than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()*
+is called.
+
 RETURN VALUE
 ------------
 The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
@@ -52,56 +66,89 @@  EXAMPLE
 -------
 [source,c]
 --
+#include <unistd.h>
 #include <tracefs.h>
+#include <stdbool.h>
 
-char **systems = tracefs_event_systems(NULL);
-
-	if (systems) {
-		int i = 0;
-		/* Got registered trace systems from the top trace instance */
-		while (systems[i]) {
-			char **events = tracefs_system_events(NULL, systems[i]);
-			if (events) {
-				/* Got registered events in system[i] from the top trace instance */
-				int j = 0;
-
-				while (events[j]) {
-					/* Got event[j] in system[i] from the top trace instance */
-					j++;
-				}
-				tracefs_list_free(events);
-			}
-			i++;
-		}
-		tracefs_list_free(systems);
-	}
-....
-static int records_walk(struct tep_event *tep, struct tep_record *record, int cpu, void *context)
+struct my_struct {
+	bool		stopped;
+};
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
 {
-	/* Got recorded event on cpu */
+	struct my_struct *my_data = data;
+	static struct trace_seq seq;
+	static int counter;
+
+	if (counter++ > 10000) {
+		my_data->stopped = true;
+		return 1;
+	}
+
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	tep_print_event(event->tep, &seq, record, "%16s-%-5d [%03d] %6.1000d %s: %s\n",
+			TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU,
+			TEP_PRINT_TIME, TEP_PRINT_NAME, TEP_PRINT_INFO);
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_reset(&seq);
 	return 0;
 }
-...
-struct tep_handle *tep = tracefs_local_events(NULL);
 
-	if (!tep) {
-		/* Failed to initialise tep handler with local events */
-		...
+static int sched_callback(struct tep_event *event, struct tep_record *record,
+			  int cpu, void *data)
+{
+	static struct tep_format_field *prev_pid;
+	static struct tep_format_field *next_pid;
+	unsigned long long pid;
+	int this_pid = *(int *)data;
+
+	if (!prev_pid) {
+		prev_pid = tep_find_field(event, "prev_pid");
+		next_pid = tep_find_field(event, "next_pid");
+		if (!prev_pid || !next_pid) {
+			fprintf(stderr, "No pid fields??\n");
+			return -1;
+		}
 	}
 
-	errno = 0;
-	ret = tracefs_event_enable(NULL, "sched", NULL);
-	if (ret < 0 && !errno)
-		printf("Could not find 'sched' events\n");
-	tracefs_event_enable(NULL, "irq", "irq_handler_\(enter\|exit\)");
+	tep_read_number_field(prev_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE LEAVING!\n");
+	tep_read_number_field(next_pid, record->data, &pid);
+	if (pid == this_pid)
+		printf("WE ARE ARRIVING!\n");
+	return 0;
+}
 
-	if (tracefs_iterate_raw_events(tep, NULL, NULL, 0, records_walk, NULL) < 0) {
-		/* Error walking through the recorded raw events */
-	}
+int main (int argc, char **argv, char **env)
+{
+	struct tep_handle *tep;
+	struct tracefs_instance *instance;
+	struct my_struct my_data = { .stopped = false };
+	int this_pid = getpid();
+
+	instance = tracefs_instance_create("my-buffer");
+	if (!instance)
+		return -1;
+
+	tracefs_event_enable(instance, "sched", NULL);
+	sleep(1);
+	tracefs_event_disable(instance, NULL, NULL);
+	tep = tracefs_local_events(NULL);
+	tep_load_plugins(tep);
+	tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid);
+	tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data);
+	tracefs_instance_destroy(instance);
+
+	if (my_data.stopped)
+		printf("stopped!\n");
 
-	/* Disable all events */
-	tracefs_event_disable(NULL, NULL, NULL);
-	tep_free(tep);
+	return 0;
+}
 --
 FILES
 -----
diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt
index cc53388438e5..e4178062d319 100644
--- a/Documentation/libtracefs.txt
+++ b/Documentation/libtracefs.txt
@@ -60,6 +60,12 @@  Trace events:
 				 const char pass:[*]_system_, const char pass:[*]_event_);
 	int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_cpus_, int _cpu_size_, int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_context_);
 	void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+	int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+				  const char pass:[*]_system_, const char pass:[*]_event_name_,
+				  int (pass:[*]_callback_)(struct tep_event pass:[*],
+						  struct tep_record pass:[*],
+						  int, void pass:[*]),
+				  void pass:[*]_callback_data_):
 	struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir_);
 	struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_);
 	int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_);
diff --git a/include/tracefs-local.h b/include/tracefs-local.h
index 1286cbf800a2..4c636be8a1fe 100644
--- a/include/tracefs-local.h
+++ b/include/tracefs-local.h
@@ -23,9 +23,18 @@  struct tracefs_options_mask {
 	unsigned long long	mask;
 };
 
+struct follow_event {
+	struct tep_event	*event;
+	void			*callback_data;
+	int (*callback)(struct tep_event *,
+			struct tep_record *,
+			int, void *);
+};
+
 struct tracefs_instance {
 	struct tracefs_options_mask	supported_opts;
 	struct tracefs_options_mask	enabled_opts;
+	struct follow_event		*followers;
 	char				*trace_dir;
 	char				*name;
 	pthread_mutex_t			lock;
@@ -35,6 +44,7 @@  struct tracefs_instance {
 	int				ftrace_notrace_fd;
 	int				ftrace_marker_fd;
 	int				ftrace_marker_raw_fd;
+	int				nr_followers;
 	bool				pipe_keep_going;
 	bool				iterate_keep_going;
 };
diff --git a/include/tracefs.h b/include/tracefs.h
index 10f84a25c31e..cb64e098883a 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -126,6 +126,12 @@  int tracefs_iterate_raw_events(struct tep_handle *tep,
 						int, void *),
 				void *callback_context);
 void tracefs_iterate_stop(struct tracefs_instance *instance);
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data);
 
 char *tracefs_event_get_file(struct tracefs_instance *instance,
 			     const char *system, const char *event,
diff --git a/src/tracefs-events.c b/src/tracefs-events.c
index 592b1a01fea4..abd97da8abbd 100644
--- a/src/tracefs-events.c
+++ b/src/tracefs-events.c
@@ -20,6 +20,9 @@ 
 #include "tracefs.h"
 #include "tracefs-local.h"
 
+static struct follow_event *root_followers;
+static int nr_root_followers;
+
 struct cpu_iterate {
 	struct tracefs_cpu *tcpu;
 	struct tep_record record;
@@ -117,7 +120,36 @@  int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu)
 	return -1;
 }
 
-static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int count,
+static int call_followers(struct tracefs_instance *instance,
+			  struct tep_event *event, struct tep_record *record, int cpu)
+{
+	struct follow_event *followers;
+	int nr_followers;
+	int ret = 0;
+	int i;
+
+	if (instance) {
+		followers = instance->followers;
+		nr_followers = instance->nr_followers;
+	} else {
+		followers = root_followers;
+		nr_followers = nr_root_followers;
+	}
+
+	if (!followers)
+		return 0;
+
+	for (i = 0; i < nr_followers; i++) {
+		if (followers[i].event == event)
+			ret |= followers[i].callback(event, record,
+						     cpu, followers[i].callback_data);
+	}
+
+	return ret;
+}
+
+static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance,
+			  struct cpu_iterate *cpus, int count,
 			  int (*callback)(struct tep_event *,
 					  struct tep_record *,
 					  int, void *),
@@ -143,6 +175,8 @@  static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int
 				j = i;
 		}
 		if (j < count) {
+			if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu))
+				break;
 			if (callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
 				break;
 			cpus[j].event = NULL;
@@ -205,6 +239,69 @@  static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
 	return -1;
 }
 
+/**
+ * tracefs_follow_event - Add callback for specific events for iterators
+ * @tep: a handle to the trace event parser context
+ * @instance: The instance to follow
+ * @system: The system of the event to track
+ * @event_name: The name of the event to track
+ * @callback: The function to call when the event is hit in an iterator
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified
+ * event is hit, it will call @callback, with the following parameters:
+ *  @event: The event pointer that was found by @system and @event_name.
+ *  @record; The event instance of @event.
+ *  @cpu: The cpu that the event happened on.
+ *  @callback_data: The same as @callback_data passed to the function.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+			  const char *system, const char *event_name,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_data)
+{
+	struct follow_event **followers;
+	struct follow_event *follower;
+	struct follow_event follow;
+	int *nr_followers;
+
+	if (!tep) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	follow.event = tep_find_event_by_name(tep, system, event_name);
+	if (!follow.event) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	follow.callback = callback;
+	follow.callback_data = callback_data;
+
+	if (instance) {
+		followers = &instance->followers;
+		nr_followers = &instance->nr_followers;
+	} else {
+		followers = &root_followers;
+		nr_followers = &nr_root_followers;
+	}
+	follower = realloc(*followers, sizeof(*follower) *
+			    ((*nr_followers) + 1));
+	if (!follower)
+		return -1;
+
+	*followers = follower;
+	follower[(*nr_followers)++] = follow;
+
+	return 0;
+}
+
 static bool top_iterate_keep_going;
 
 /*
@@ -247,7 +344,7 @@  int tracefs_iterate_raw_events(struct tep_handle *tep,
 	ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
 	if (ret < 0)
 		goto out;
-	ret = read_cpu_pages(tep, all_cpus, count,
+	ret = read_cpu_pages(tep, instance, all_cpus, count,
 			     callback, callback_context,
 			     keep_going);