Linux-Trace-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v2 0/4] Iterate over raw events in sorted order
@ 2021-04-07  3:48 Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 1/4] libtracefs: " Tzvetomir Stoyanov (VMware)
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2021-04-07  3:48 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

tracefs_iterate_raw_events(): read oldest events first.
Updated the unit tests and documentation according to the new logic of
the function.

v2 changes:
 - Conding style fixes.
 - Fixed typo in function name.

Tzvetomir Stoyanov (VMware) (4):
  libtracefs: Iterate over raw events in sorted order
  libtracefs: Unit test for iterate over raw events in sorted order
  libtracefs: Unit test for iterate over raw events in selected CPUs
  libtracefs: tracefs_iterate_raw_events() documentation update

 Documentation/libtracefs-events.txt |  14 +-
 src/tracefs-events.c                | 247 ++++++++++++++++++----------
 utest/tracefs-utest.c               |  31 +++-
 3 files changed, 190 insertions(+), 102 deletions(-)

-- 
2.30.2


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

* [PATCH v2 1/4] libtracefs: Iterate over raw events in sorted order
  2021-04-07  3:48 [PATCH v2 0/4] Iterate over raw events in sorted order Tzvetomir Stoyanov (VMware)
@ 2021-04-07  3:48 ` Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 2/4] libtracefs: Unit test for iterate " Tzvetomir Stoyanov (VMware)
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2021-04-07  3:48 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Changed the logic of tracefs_iterate_raw_events(): instead of iterating
in CPU order, walk through the events from all CPU buffers and read the
oldest first.
Fixed the problem with cpu number in the record, passed to the callback
- set the real CPU number instead of 0.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 src/tracefs-events.c | 247 +++++++++++++++++++++++++++----------------
 1 file changed, 156 insertions(+), 91 deletions(-)

diff --git a/src/tracefs-events.c b/src/tracefs-events.c
index 825f916..da56943 100644
--- a/src/tracefs-events.c
+++ b/src/tracefs-events.c
@@ -51,120 +51,129 @@ page_to_kbuf(struct tep_handle *tep, void *page, int size)
 	return kbuf;
 }
 
-static int read_kbuf_record(struct kbuffer *kbuf, struct tep_record *record)
+struct cpu_iterate {
+	struct tep_record record;
+	struct tep_event *event;
+	struct kbuffer *kbuf;
+	void *page;
+	int psize;
+	int rsize;
+	int cpu;
+	int fd;
+};
+
+static int read_kbuf_record(struct cpu_iterate *cpu)
 {
 	unsigned long long ts;
 	void *ptr;
 
-	ptr = kbuffer_read_event(kbuf, &ts);
-	if (!ptr || !record)
+	if (!cpu || !cpu->kbuf)
+		return -1;
+	ptr = kbuffer_read_event(cpu->kbuf, &ts);
+	if (!ptr)
 		return -1;
 
-	memset(record, 0, sizeof(*record));
-	record->ts = ts;
-	record->size = kbuffer_event_size(kbuf);
-	record->record_size = kbuffer_curr_size(kbuf);
-	record->cpu = 0;
-	record->data = ptr;
-	record->ref_count = 1;
+	memset(&cpu->record, 0, sizeof(cpu->record));
+	cpu->record.ts = ts;
+	cpu->record.size = kbuffer_event_size(cpu->kbuf);
+	cpu->record.record_size = kbuffer_curr_size(cpu->kbuf);
+	cpu->record.cpu = cpu->cpu;
+	cpu->record.data = ptr;
+	cpu->record.ref_count = 1;
 
-	kbuffer_next_event(kbuf, NULL);
+	kbuffer_next_event(cpu->kbuf, NULL);
 
 	return 0;
 }
 
-static int
-get_events_in_page(struct tep_handle *tep, void *page,
-		   int size, int cpu,
-		   int (*callback)(struct tep_event *,
-				   struct tep_record *,
-				   int, void *),
-		   void *callback_context)
+int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu)
 {
-	struct tep_record record;
-	struct tep_event *event;
-	struct kbuffer *kbuf;
-	int id, cnt = 0;
+	cpu->rsize = read(cpu->fd, cpu->page, cpu->psize);
+	if (cpu->rsize <= 0)
+		return -1;
+
+	cpu->kbuf = page_to_kbuf(tep, cpu->page, cpu->rsize);
+	if (!cpu->kbuf)
+		return -1;
+
+	return 0;
+}
+
+int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu)
+{
+	int id;
+
+	do {
+		while (!read_kbuf_record(cpu)) {
+			id = tep_data_type(tep, &(cpu->record));
+			cpu->event = tep_find_event(tep, id);
+			if (cpu->event)
+				return 0;
+		}
+	} while (!read_next_page(tep, cpu));
+
+	return -1;
+}
+
+static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int count,
+			  int (*callback)(struct tep_event *,
+					  struct tep_record *,
+					  int, void *),
+			  void *callback_context)
+{
+	bool has_data = false;
 	int ret;
+	int i, j;
 
-	if (size <= 0)
-		return 0;
+	for (i = 0; i < count; i++) {
+		ret = read_next_record(tep, cpus + i);
+		if (!ret)
+			has_data = true;
+	}
 
-	kbuf = page_to_kbuf(tep, page, size);
-	if (!kbuf)
-		return 0;
-
-	ret = read_kbuf_record(kbuf, &record);
-	while (!ret) {
-		id = tep_data_type(tep, &record);
-		event = tep_find_event(tep, id);
-		if (event) {
-			cnt++;
-			if (callback &&
-			    callback(event, &record, cpu, callback_context))
+	while (has_data) {
+		j = count;
+		for (i = 0; i < count; i++) {
+			if (!cpus[i].event)
+				continue;
+			if (j == count || cpus[j].record.ts > cpus[i].record.ts)
+				j = i;
+		}
+		if (j < count) {
+			if (callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
 				break;
+			cpus[j].event = NULL;
+			read_next_record(tep, cpus + j);
+		} else {
+			has_data = false;
 		}
-		ret = read_kbuf_record(kbuf, &record);
 	}
 
-	kbuffer_free(kbuf);
-
-	return cnt;
+	return 0;
 }
 
-/*
- * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw,
- *				per CPU trace buffers
- * @tep: a handle to the trace event parser context
- * @instance: ftrace instance, can be NULL for the top instance
- * @cpus: Iterate only through the buffers of CPUs, set in the mask.
- *	  If NULL, iterate through all CPUs.
- * @cpu_size: size of @cpus set
- * @callback: A user function, called for each record from the file
- * @callback_context: A custom context, passed to the user callback function
- *
- * If the @callback returns non-zero, the iteration stops - in that case all
- * records from the current page will be lost from future reads
- *
- * Returns -1 in case of an error, or 0 otherwise
- */
-int tracefs_iterate_raw_events(struct tep_handle *tep,
-				struct tracefs_instance *instance,
-				cpu_set_t *cpus, int cpu_size,
-				int (*callback)(struct tep_event *,
-						struct tep_record *,
-						int, void *),
-				void *callback_context)
+static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
+			  int cpu_size, struct cpu_iterate **all_cpus, int *count)
 {
+	struct cpu_iterate *tmp;
 	unsigned int p_size;
 	struct dirent *dent;
 	char file[PATH_MAX];
-	void *page = NULL;
 	struct stat st;
+	int ret = -1;
+	int fd = -1;
 	char *path;
 	DIR *dir;
-	int ret;
 	int cpu;
-	int fd;
-	int r;
+	int i = 0;
 
-	if (!tep || !callback)
-		return -1;
-
-	p_size = getpagesize();
 	path = tracefs_instance_get_file(instance, "per_cpu");
 	if (!path)
 		return -1;
 	dir = opendir(path);
-	if (!dir) {
-		ret = -1;
-		goto error;
-	}
-	page = malloc(p_size);
-	if (!page) {
-		ret = -1;
-		goto error;
-	}
+	if (!dir)
+		goto out;
+	p_size = getpagesize();
 	while ((dent = readdir(dir))) {
 		const char *name = dent->d_name;
 
@@ -174,32 +183,88 @@ int tracefs_iterate_raw_events(struct tep_handle *tep,
 		if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
 			continue;
 		sprintf(file, "%s/%s", path, name);
-		ret = stat(file, &st);
-		if (ret < 0 || !S_ISDIR(st.st_mode))
+		if (stat(file, &st) < 0 || !S_ISDIR(st.st_mode))
 			continue;
 
 		sprintf(file, "%s/%s/trace_pipe_raw", path, name);
 		fd = open(file, O_RDONLY | O_NONBLOCK);
 		if (fd < 0)
 			continue;
-		do {
-			r = read(fd, page, p_size);
-			if (r > 0)
-				get_events_in_page(tep, page, r, cpu,
-						   callback, callback_context);
-		} while (r > 0);
-		close(fd);
+		tmp = realloc(*all_cpus, (i + 1) * sizeof(struct cpu_iterate));
+		if (!tmp) {
+			close(fd);
+			goto out;
+		}
+		memset(tmp + i, 0, sizeof(struct cpu_iterate));
+		tmp[i].fd = fd;
+		tmp[i].cpu = cpu;
+		tmp[i].page =  malloc(p_size);
+		tmp[i].psize = p_size;
+		*all_cpus = tmp;
+		*count = i + 1;
+		if (!tmp[i++].page)
+			goto out;
 	}
+
 	ret = 0;
 
-error:
+out:
 	if (dir)
 		closedir(dir);
-	free(page);
 	tracefs_put_tracing_file(path);
 	return ret;
 }
 
+/*
+ * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw,
+ *				per CPU trace buffers
+ * @tep: a handle to the trace event parser context
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @cpus: Iterate only through the buffers of CPUs, set in the mask.
+ *	  If NULL, iterate through all CPUs.
+ * @cpu_size: size of @cpus set
+ * @callback: A user function, called for each record from the file
+ * @callback_context: A custom context, passed to the user callback function
+ *
+ * If the @callback returns non-zero, the iteration stops - in that case all
+ * records from the current page will be lost from future reads
+ * The events are iterated in sorted order, oldest first.
+ *
+ * Returns -1 in case of an error, or 0 otherwise
+ */
+int tracefs_iterate_raw_events(struct tep_handle *tep,
+				struct tracefs_instance *instance,
+				cpu_set_t *cpus, int cpu_size,
+				int (*callback)(struct tep_event *,
+						struct tep_record *,
+						int, void *),
+				void *callback_context)
+{
+	struct cpu_iterate *all_cpus = NULL;
+	int count = 0;
+	int ret;
+	int i;
+
+	if (!tep || !callback)
+		return -1;
+
+	ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
+	if (ret < 0)
+		goto out;
+	ret = read_cpu_pages(tep, all_cpus, count, callback, callback_context);
+
+out:
+	if (all_cpus) {
+		for (i = 0; i < count; i++) {
+			close(all_cpus[i].fd);
+			free(all_cpus[i].page);
+		}
+		free(all_cpus);
+	}
+
+	return ret;
+}
+
 static char **add_list_string(char **list, const char *name, int len)
 {
 	if (!list)
-- 
2.30.2


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

* [PATCH v2 2/4] libtracefs: Unit test for iterate over raw events in sorted order
  2021-04-07  3:48 [PATCH v2 0/4] Iterate over raw events in sorted order Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 1/4] libtracefs: " Tzvetomir Stoyanov (VMware)
@ 2021-04-07  3:48 ` Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 3/4] libtracefs: Unit test for iterate over raw events in selected CPUs Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 4/4] libtracefs: tracefs_iterate_raw_events() documentation update Tzvetomir Stoyanov (VMware)
  3 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2021-04-07  3:48 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Enhanced the unit test of tracefs_iterate_raw_events() API to check if
events are iterated in sorted order: latest first. Added a check for the
correct CPU number in the record, passed to the callback.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 utest/tracefs-utest.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index ed2693b..fea4e1f 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -20,7 +20,7 @@
 #define TRACEFS_SUITE		"trasefs library"
 #define TEST_INSTANCE_NAME	"cunit_test_iter"
 #define TEST_TRACE_DIR		"/tmp/trace_utest.XXXXXX"
-#define TEST_ARRAY_SIZE		500
+#define TEST_ARRAY_SIZE		5000
 
 #define ALL_TRACERS	"available_tracers"
 #define CUR_TRACER	"current_tracer"
@@ -36,6 +36,7 @@ struct test_sample {
 };
 static struct test_sample test_array[TEST_ARRAY_SIZE];
 static int test_found;
+static unsigned long long last_ts;
 
 static int test_callback(struct tep_event *event, struct tep_record *record,
 			  int cpu, void *context)
@@ -45,8 +46,13 @@ static int test_callback(struct tep_event *event, struct tep_record *record,
 	int *cpu_test = (int *)context;
 	int i;
 
+	CU_TEST(last_ts <= record->ts);
+	last_ts = record->ts;
+
 	if (cpu_test && *cpu_test >= 0 && *cpu_test != cpu)
 		return 0;
+	CU_TEST(cpu == record->cpu);
+
 	field = tep_find_field(event, "buf");
 	if (field) {
 		sample = ((struct test_sample *)(record->data + field->offset));
@@ -136,6 +142,7 @@ static void test_instance_iter_raw_events(struct tracefs_instance *instance)
 
 	ret = tracefs_iterate_raw_events(NULL, instance, NULL, 0, test_callback, NULL);
 	CU_TEST(ret < 0);
+	last_ts = 0;
 	ret = tracefs_iterate_raw_events(test_tep, NULL, NULL, 0, test_callback, NULL);
 	CU_TEST(ret == 0);
 	ret = tracefs_iterate_raw_events(test_tep, instance, NULL, 0, NULL, NULL);
-- 
2.30.2


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

* [PATCH v2 3/4] libtracefs: Unit test for iterate over raw events in selected CPUs
  2021-04-07  3:48 [PATCH v2 0/4] Iterate over raw events in sorted order Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 1/4] libtracefs: " Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 2/4] libtracefs: Unit test for iterate " Tzvetomir Stoyanov (VMware)
@ 2021-04-07  3:48 ` Tzvetomir Stoyanov (VMware)
  2021-04-07  3:48 ` [PATCH v2 4/4] libtracefs: tracefs_iterate_raw_events() documentation update Tzvetomir Stoyanov (VMware)
  3 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2021-04-07  3:48 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Enhanced the unit test of tracefs_iterate_raw_events() API to walk
through the selected CPUs only. Added some forgotten calls to CPU_FREE()
for freeing used CPU sets.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 utest/tracefs-utest.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index fea4e1f..f9441af 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -49,8 +49,9 @@ static int test_callback(struct tep_event *event, struct tep_record *record,
 	CU_TEST(last_ts <= record->ts);
 	last_ts = record->ts;
 
-	if (cpu_test && *cpu_test >= 0 && *cpu_test != cpu)
-		return 0;
+	if (cpu_test && *cpu_test >= 0) {
+		CU_TEST(*cpu_test == cpu);
+	}
 	CU_TEST(cpu == record->cpu);
 
 	field = tep_find_field(event, "buf");
@@ -105,18 +106,30 @@ static void test_iter_write(struct tracefs_instance *instance)
 
 	sched_setaffinity(0, cpu_size, cpusave);
 	close(fd);
+	CPU_FREE(cpuset);
+	CPU_FREE(cpusave);
 }
 
 
 static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu)
 {
+	int cpus = sysconf(_SC_NPROCESSORS_CONF);
+	cpu_set_t *cpuset = NULL;
+	int cpu_size = 0;
 	int check = 0;
 	int ret;
 	int i;
 
+	if (cpu >= 0) {
+		cpuset = CPU_ALLOC(cpus);
+		cpu_size = CPU_ALLOC_SIZE(cpus);
+		CPU_ZERO_S(cpu_size, cpuset);
+		CPU_SET(cpu, cpuset);
+	}
 	test_found = 0;
+	last_ts = 0;
 	test_iter_write(instance);
-	ret = tracefs_iterate_raw_events(test_tep, instance, NULL, 0,
+	ret = tracefs_iterate_raw_events(test_tep, instance, cpuset, cpu_size,
 					 test_callback, &cpu);
 	CU_TEST(ret == 0);
 	if (cpu < 0) {
@@ -132,6 +145,9 @@ static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu)
 		}
 		CU_TEST(test_found == check);
 	}
+
+	if (cpuset)
+		CPU_FREE(cpuset);
 }
 
 static void test_instance_iter_raw_events(struct tracefs_instance *instance)
-- 
2.30.2


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

* [PATCH v2 4/4] libtracefs: tracefs_iterate_raw_events() documentation update
  2021-04-07  3:48 [PATCH v2 0/4] Iterate over raw events in sorted order Tzvetomir Stoyanov (VMware)
                   ` (2 preceding siblings ...)
  2021-04-07  3:48 ` [PATCH v2 3/4] libtracefs: Unit test for iterate over raw events in selected CPUs Tzvetomir Stoyanov (VMware)
@ 2021-04-07  3:48 ` Tzvetomir Stoyanov (VMware)
  3 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2021-04-07  3:48 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Updated the man page of tracefs_iterate_raw_events() API:
 - Fixed a typo in the function name.
 - Added a new sentence, describing the iteration in sorted order.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 Documentation/libtracefs-events.txt | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/Documentation/libtracefs-events.txt b/Documentation/libtracefs-events.txt
index 9608a12..78c9834 100644
--- a/Documentation/libtracefs-events.txt
+++ b/Documentation/libtracefs-events.txt
@@ -37,14 +37,14 @@ local machine, or it may be a path to a copy of the tracefs directory
 from another machine. The last entry in the array as a NULL pointer.
 The array must be freed with _tracefs_list_free()_ API.
 
-The _tracefs_interate_raw_events()_ function will read the tracefs raw
+The _tracefs_iterate_raw_events()_ function will read the tracefs raw
 data buffers and call the specified _callback_ function for every event it
-encounters. An initialized _tep_ handler is required (See
-_tracefs_local_events_(3)). If _instance_ is NULL, then the toplevel
-tracefs buffer is used, otherwise the buffer for the corresponding
-_instance_ is read. To filter only on a subset of CPUs, _cpus_ and
-_cpu_size_ may be set to only call _callback_ with events that occurred on
-the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
+encounters. Events are iterated in sorted order: oldest first. An initialized
+_tep_ handler is required (See _tracefs_local_events_(3)). If _instance_ is
+NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
+the corresponding _instance_ is read. To filter only on a subset of CPUs,
+_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
+occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
 function will be called for all events, and _cpu_size_ is ignored. The
 _callback_ function will be called with the following parameters: A
 pointer to a struct tep_event that corresponds to the type of event the
-- 
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-04-07  3:48 [PATCH v2 0/4] Iterate over raw events in sorted order Tzvetomir Stoyanov (VMware)
2021-04-07  3:48 ` [PATCH v2 1/4] libtracefs: " Tzvetomir Stoyanov (VMware)
2021-04-07  3:48 ` [PATCH v2 2/4] libtracefs: Unit test for iterate " Tzvetomir Stoyanov (VMware)
2021-04-07  3:48 ` [PATCH v2 3/4] libtracefs: Unit test for iterate over raw events in selected CPUs Tzvetomir Stoyanov (VMware)
2021-04-07  3:48 ` [PATCH v2 4/4] libtracefs: tracefs_iterate_raw_events() documentation update Tzvetomir Stoyanov (VMware)

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