All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] libtracefs: Uprobe APIs
@ 2022-03-28  9:03 Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 1/3] libtracefs: New APIs for ftrace uprobes Tzvetomir Stoyanov (VMware)
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2022-03-28  9:03 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Ftrace uprobe is the only dynamic event with no corresponding library API.
Add new APIs for allocating uprobes.

Tzvetomir Stoyanov (VMware) (3):
  libtracefs: New APIs for ftrace uprobes
  libtracefs: Document uprobes APIs
  libtracefs: Unit tests for uprobes APIs

 Documentation/libtracefs-uprobes.txt | 188 +++++++++++++++++++++++++++
 include/tracefs.h                    |   6 +
 src/Makefile                         |   1 +
 src/tracefs-uprobes.c                |  90 +++++++++++++
 utest/tracefs-utest.c                |  74 +++++++++++
 5 files changed, 359 insertions(+)
 create mode 100644 Documentation/libtracefs-uprobes.txt
 create mode 100644 src/tracefs-uprobes.c

-- 
2.35.1


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

* [PATCH 1/3] libtracefs: New APIs for ftrace uprobes
  2022-03-28  9:03 [PATCH 0/3] libtracefs: Uprobe APIs Tzvetomir Stoyanov (VMware)
@ 2022-03-28  9:03 ` Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 2/3] libtracefs: Document uprobes APIs Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 3/3] libtracefs: Unit tests for " Tzvetomir Stoyanov (VMware)
  2 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2022-03-28  9:03 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The tracefs library has APIs for allocating almost all types of ftrace
dynamic events. Uprobe is the only dynamic event that has no
corresponding tracefs API. These two APIs are proposed:
 tracefs_uprobe_alloc()
 tracefs_uretprobe_alloc()
They allocate context for new uprobe/uretprobe, without creating events
in the system. The regular dynamic events APIs can be used to work with
newly created uprobes.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/tracefs.h     |  6 +++
 src/Makefile          |  1 +
 src/tracefs-uprobes.c | 90 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 src/tracefs-uprobes.c

diff --git a/include/tracefs.h b/include/tracefs.h
index 05f3352..27954a4 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -280,6 +280,12 @@ tracefs_dynevent_get_event(struct tep_handle *tep, struct tracefs_dynevent *dyne
 struct tracefs_dynevent *
 tracefs_eprobe_alloc(const char *system, const char *event,
 		     const char *target_system, const char *target_event, const char *fetchargs);
+struct tracefs_dynevent *
+tracefs_uprobe_alloc(const char *system, const char *event,
+		     const char *file, unsigned long long offset, const char *fetchargs);
+struct tracefs_dynevent *
+tracefs_uretprobe_alloc(const char *system, const char *event,
+			const char *file, unsigned long long offset, const char *fetchargs);
 
 struct tracefs_dynevent *
 tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format);
diff --git a/src/Makefile b/src/Makefile
index e8afab5..645d518 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -13,6 +13,7 @@ OBJS += tracefs-hist.o
 OBJS += tracefs-filter.o
 OBJS += tracefs-dynevents.o
 OBJS += tracefs-eprobes.o
+OBJS += tracefs-uprobes.o
 
 # Order matters for the the three below
 OBJS += sqlhist-lex.o
diff --git a/src/tracefs-uprobes.c b/src/tracefs-uprobes.c
new file mode 100644
index 0000000..aa39b75
--- /dev/null
+++ b/src/tracefs-uprobes.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define UPROBE_DEFAULT_GROUP "uprobes"
+
+static struct tracefs_dynevent *
+uprobe_alloc(enum tracefs_dynevent_type type, const char *system, const char *event,
+	     const char *file, unsigned long long offset, const char *fetchargs)
+{
+	struct tracefs_dynevent *kp;
+	char *target;
+
+	if (!event || !file) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!system)
+		system = UPROBE_DEFAULT_GROUP;
+
+	if (asprintf(&target, "%s:0x%0*llx", file, (int)(sizeof(void *) * 2), offset) < 0)
+		return NULL;
+
+	kp = dynevent_alloc(type, system, event, target, fetchargs);
+	free(target);
+
+	return kp;
+}
+
+/**
+ * tracefs_uprobe_alloc - Allocate new user probe (uprobe)
+ * @system: The system name (NULL for the default uprobes)
+ * @event: The name of the event to create
+ * @file: The full path to the binary file, where the uprobe will be set
+ * @offset: Offset within the @file
+ * @fetchargs: String with arguments, that will be fetched with the uprobe
+ *
+ * Allocate new uprobe context that will be in the @system group
+ * (or uprobes if @system is NULL) and with @event name. The new uprobe will be
+ * attached to @offset within the @file. The arguments described in @fetchargs
+ * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst
+ * for more details.
+ *
+ * The uprobe is not created in the system.
+ *
+ * Return a pointer to a uprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ */
+struct tracefs_dynevent *
+tracefs_uprobe_alloc(const char *system, const char *event,
+		     const char *file, unsigned long long offset, const char *fetchargs)
+{
+	return uprobe_alloc(TRACEFS_DYNEVENT_UPROBE, system, event, file, offset, fetchargs);
+}
+
+/**
+ * tracefs_uretprobe_alloc - Allocate new user return probe (uretprobe)
+ * @system: The system name (NULL for the default uprobes)
+ * @event: The name of the event to create
+ * @file: The full path to the binary file, where the uretprobe will be set
+ * @offset: Offset within the @file
+ * @fetchargs: String with arguments, that will be fetched with the uretprobe
+ *
+ * Allocate mew uretprobe context that will be in the @system group
+ * (or uprobes if @system is NULL) and with @event name. The new uretprobe will
+ * be attached to @offset within the @file. The arguments described in @fetchargs
+ * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst
+ * for more details.
+ *
+ * The uretprobe is not created in the system.
+ *
+ * Return a pointer to a uretprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_dynevent_free()
+ *
+ */
+struct tracefs_dynevent *
+tracefs_uretprobe_alloc(const char *system, const char *event,
+			const char *file, unsigned long long offset, const char *fetchargs)
+{
+	return uprobe_alloc(TRACEFS_DYNEVENT_URETPROBE, system, event, file, offset, fetchargs);
+}
-- 
2.35.1


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

* [PATCH 2/3] libtracefs: Document uprobes APIs
  2022-03-28  9:03 [PATCH 0/3] libtracefs: Uprobe APIs Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 1/3] libtracefs: New APIs for ftrace uprobes Tzvetomir Stoyanov (VMware)
@ 2022-03-28  9:03 ` Tzvetomir Stoyanov (VMware)
  2022-04-04 17:51   ` Steven Rostedt
  2022-03-28  9:03 ` [PATCH 3/3] libtracefs: Unit tests for " Tzvetomir Stoyanov (VMware)
  2 siblings, 1 reply; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2022-03-28  9:03 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The newly introduced uprobe APIs should be described in a man page.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 Documentation/libtracefs-uprobes.txt | 188 +++++++++++++++++++++++++++
 1 file changed, 188 insertions(+)
 create mode 100644 Documentation/libtracefs-uprobes.txt

diff --git a/Documentation/libtracefs-uprobes.txt b/Documentation/libtracefs-uprobes.txt
new file mode 100644
index 0000000..dae0fc9
--- /dev/null
+++ b/Documentation/libtracefs-uprobes.txt
@@ -0,0 +1,188 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_uprobe_alloc,tracefs_uretprobe_alloc - Allocate new user (return) probe
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_dynevent pass:[*]
+*tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+struct tracefs_dynevent pass:[*]
+*tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+		     const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+--
+
+DESCRIPTION
+-----------
+*tracefs_uprobe_alloc*() allocates a new uprobe context. It will be in the _system_ group
+(or uprobes if _system_ is NULL) and with _event_ name. The uprobe will be attached to _offset_
+within the _file_. The list of arguments, described in _fetchargs_, will be fetched  with the uprobe.
+The returned pointer to the user probe context must be freed with *tracefs_dynevent_free*().
+The ubrobe is not configured in the system, tracefs_dynevent_* set of APIs can be used to configure
+it.
+
+The *tracefs_uretprobe_alloc*() behaves the same as *tracefs_uprobe_alloc*(), the only difference is
+that it allocates context to user return probe (uretprobe).
+
+RETURN VALUE
+------------
+The *tracefs_uprobe_alloc*() and *tracefs_uretprobe_alloc*() APIs return a pointer to an allocated
+tracefs_dynevent structure, describing the user probe. This pointer must be freed with
+*tracefs_dynevent_free*(3). Note, this only allocates a descriptor representing the uprobe. It does
+not modify the running system. On error NULL is returned.
+
+EXAMPLE
+-------
+[source,c]
+--
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs.h>
+
+static int callback(struct tep_event *event, struct tep_record *record,
+		    int cpu, void *data)
+{
+	struct trace_seq seq;
+
+	trace_seq_init(&seq);
+	tep_print_event(event->tep, &seq, record, "%d-%s: %s",
+			TEP_PRINT_PID, TEP_PRINT_COMM, TEP_PRINT_NAME);
+	trace_seq_puts(&seq, "'\n");
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+	trace_seq_destroy(&seq);
+
+	return 0;
+}
+
+static pid_t run_exec(char **argv, char **env)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid)
+		return pid;
+
+	execve(argv[0], argv, env);
+	perror("exec");
+	exit(-1);
+}
+
+const char *myprobe = "my_urobes";
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_dynevent *uprobe, *uretprobe;
+	struct tep_handle *tep;
+	struct tracefs_instance *instance;
+	const char *sysnames[] = { myprobe, NULL };
+	long addr;
+	pid_t pid;
+
+	if (argc < 3) {
+		printf("usage: %s file_offset command\n", argv[0]);
+		exit(-1);
+	}
+	addr = strtol(argv[1], NULL, 0);
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_UPROBE|TRACEFS_DYNEVENT_URETPROBE, true);
+
+	uprobe = tracefs_uprobe_alloc(myprobe, "user_probe", argv[2], addr, NULL);
+	uretprobe = tracefs_uretprobe_alloc(myprobe, "user_retprobe", argv[2], addr, NULL);
+	if (!uprobe || !uretprobe) {
+		perror("allocating user probes");
+		exit(-1);
+	}
+
+	if (tracefs_dynevent_create(uprobe) ||
+	    tracefs_dynevent_create(uretprobe)) {
+		perror("creating user probes");
+		exit(-1);
+	}
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+
+	tracefs_event_enable(instance, myprobe, "user_probe");
+	tracefs_event_enable(instance, myprobe, "user_retprobe");
+
+	pid = run_exec(&argv[2], env);
+
+	/* Let the child start to run */
+	sched_yield();
+
+	do {
+		tracefs_load_cmdlines(NULL, tep);
+		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
+	} while (waitpid(pid, NULL, WNOHANG) != pid);
+
+	/* disable and destroy the events */
+	tracefs_dynevent_destroy(uprobe, true);
+	tracefs_dynevent_destroy(uretprobe, true);
+	tracefs_dynevent_free(uprobe);
+	tracefs_dynevent_free(uretprobe);
+	tracefs_instance_destroy(instance);
+
+	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)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@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) 2022 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
-- 
2.35.1


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

* [PATCH 3/3] libtracefs: Unit tests for uprobes APIs
  2022-03-28  9:03 [PATCH 0/3] libtracefs: Uprobe APIs Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 1/3] libtracefs: New APIs for ftrace uprobes Tzvetomir Stoyanov (VMware)
  2022-03-28  9:03 ` [PATCH 2/3] libtracefs: Document uprobes APIs Tzvetomir Stoyanov (VMware)
@ 2022-03-28  9:03 ` Tzvetomir Stoyanov (VMware)
  2 siblings, 0 replies; 5+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2022-03-28  9:03 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

The newly introduced uprobes APIs should be covered by the library unit
tests.

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

diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index 7042fa9..3f63837 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -873,6 +873,79 @@ static void test_eprobes(void)
 	test_eprobes_instance(test_instance);
 }
 
+#define FOFFSET 1000ll
+static void test_uprobes_instance(struct tracefs_instance *instance)
+{
+	struct probe_test utests[] = {
+		{ TRACEFS_DYNEVENT_UPROBE, "p", "utest", "utest_u", NULL, "arg1=$stack2" },
+		{ TRACEFS_DYNEVENT_URETPROBE, "r", "utest", "utest_r", NULL, "arg1=$retval" },
+	};
+	int count = sizeof(utests) / sizeof((utests)[0]);
+	struct tracefs_dynevent **duprobes;
+	struct tracefs_dynevent **duvents;
+	char self[PATH_MAX] = { 0 };
+	struct tep_handle *tep;
+	char *target = NULL;
+	int i;
+
+	tep = tep_alloc();
+	CU_TEST(tep != NULL);
+
+	duprobes = calloc(count + 1, sizeof(*duvents));
+	CU_TEST(duprobes != NULL);
+	CU_TEST(readlink("/proc/self/exe", self, sizeof(self)) > 0);
+	CU_TEST(asprintf(&target, "%s:0x%0*llx", self, (int)(sizeof(void *) * 2), FOFFSET) > 0);
+
+	for (i = 0; i < count; i++)
+		utests[i].address = target;
+
+	/* Invalid parameters */
+	CU_TEST(tracefs_uprobe_alloc(NULL, NULL, self, 0, NULL) == NULL);
+	CU_TEST(tracefs_uprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL);
+	CU_TEST(tracefs_uretprobe_alloc(NULL, NULL, self, 0, NULL) == NULL);
+	CU_TEST(tracefs_uretprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL);
+
+	for (i = 0; i < count; i++) {
+		if (utests[i].type == TRACEFS_DYNEVENT_UPROBE)
+			duprobes[i] = tracefs_uprobe_alloc(utests[i].system, utests[i].event,
+							   self, FOFFSET, utests[i].format);
+		else
+			duprobes[i] = tracefs_uretprobe_alloc(utests[i].system, utests[i].event,
+							      self, FOFFSET, utests[i].format);
+		CU_TEST(duprobes[i] != NULL);
+	}
+	duprobes[i] = NULL;
+
+	get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0);
+	CU_TEST(check_probes(utests, count, duprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_create(duprobes[i]) == 0);
+	}
+
+	duvents = get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, count);
+	CU_TEST(check_probes(utests, count, duvents, true, instance, tep));
+	tracefs_dynevent_list_free(duvents);
+
+	for (i = 0; i < count; i++) {
+		CU_TEST(tracefs_dynevent_destroy(duprobes[i], false) == 0);
+	}
+	get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0);
+	CU_TEST(check_probes(utests, count, duprobes, false, instance, tep));
+
+	for (i = 0; i < count; i++)
+		tracefs_dynevent_free(duprobes[i]);
+
+	free(duprobes);
+	free(target);
+	tep_free(tep);
+}
+
+static void test_uprobes(void)
+{
+	test_uprobes_instance(test_instance);
+}
+
 static void test_instance_file(void)
 {
 	struct tracefs_instance *instance = NULL;
@@ -1708,4 +1781,5 @@ void test_tracefs_lib(void)
 	CU_add_test(suite, "kprobes", test_kprobes);
 	CU_add_test(suite, "synthetic events", test_synthetic);
 	CU_add_test(suite, "eprobes", test_eprobes);
+	CU_add_test(suite, "uprobes", test_uprobes);
 }
-- 
2.35.1


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

* Re: [PATCH 2/3] libtracefs: Document uprobes APIs
  2022-03-28  9:03 ` [PATCH 2/3] libtracefs: Document uprobes APIs Tzvetomir Stoyanov (VMware)
@ 2022-04-04 17:51   ` Steven Rostedt
  0 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2022-04-04 17:51 UTC (permalink / raw)
  To: Tzvetomir Stoyanov (VMware); +Cc: linux-trace-devel

On Mon, 28 Mar 2022 12:03:46 +0300
"Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com> wrote:

> +	do {
> +		tracefs_load_cmdlines(NULL, tep);
> +		tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL);
> +	} while (waitpid(pid, NULL, WNOHANG) != pid);
> +
> +	/* disable and destroy the events */
> +	tracefs_dynevent_destroy(uprobe, true);
> +	tracefs_dynevent_destroy(uretprobe, true);
> +	tracefs_dynevent_free(uprobe);
> +	tracefs_dynevent_free(uretprobe);
> +	tracefs_instance_destroy(instance);
> +
> +	return 0;

I added the missing brace here.

-- Steve

> +--
> +
> +FILES
> +-----
> +[verse]

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

end of thread, other threads:[~2022-04-04 21:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-28  9:03 [PATCH 0/3] libtracefs: Uprobe APIs Tzvetomir Stoyanov (VMware)
2022-03-28  9:03 ` [PATCH 1/3] libtracefs: New APIs for ftrace uprobes Tzvetomir Stoyanov (VMware)
2022-03-28  9:03 ` [PATCH 2/3] libtracefs: Document uprobes APIs Tzvetomir Stoyanov (VMware)
2022-04-04 17:51   ` Steven Rostedt
2022-03-28  9:03 ` [PATCH 3/3] libtracefs: Unit tests for " Tzvetomir Stoyanov (VMware)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.