All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes
@ 2021-07-02 20:17 Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 01/10] libtracefs: Implement tracefs_instances() Steven Rostedt
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt

Add a tracefs_kprobe_raw() to facilitate adding kprobe events. It's
denoted as "raw" because it still requires knowing the complex format of
a kprobe, but at least it helps with other formats:

 p:[[system/]event] addr fmt

The user only needs to know the "fmt" part above, and not worry about
what file to open, or how to open it.

Also add a tracefs_kprobe_clear() to clear all kprobes and a
tracefs_kprobe_clear_probe() to clear an individual kprobe. Both have a
"force" parameter, that if set, will then try to disable the kprobe in
all instances (including the top) and then remove the kprobe(s).

Changes since v6: https://lore.kernel.org/linux-trace-devel/20210702185616.167778-1-rostedt@goodmis.org/

 - Fixed some utest cases that used strcmp() when it should have used
   !strcmp().

This should be the last version before I push (I hope!)

Steven Rostedt (VMware) (10):
  libtracefs: Implement tracefs_instances()
  libtracefs: Implement tracefs_kprobe_raw()
  libtracefs: Implement tracefs_kretprobe_raw()
  libtracefs: Implement tracefs_get_kprobes()
  libtracefs: Add helper function to parse kprobes
  libtracefs: Implement tracefs_kprobe_info()
  libtracefs: Implement tracefs_kprobe_clear_all() to remove all kprobes
  libtracefs: Implement tracefs_kprobe_clear_probe()
  libtracefs: Add man pages for kprobe functions
  libtracefs: Update the unit tests to use the kprobe API instead

 Documentation/libtracefs-kprobes.txt | 297 ++++++++++++++++
 include/tracefs.h                    |  17 +
 src/Makefile                         |   1 +
 src/tracefs-instance.c               |  76 +++++
 src/tracefs-kprobes.c                | 485 +++++++++++++++++++++++++++
 utest/tracefs-utest.c                | 195 +++++++++--
 6 files changed, 1042 insertions(+), 29 deletions(-)
 create mode 100644 Documentation/libtracefs-kprobes.txt
 create mode 100644 src/tracefs-kprobes.c

-- 
2.30.2


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

* [PATCH v7 01/10] libtracefs: Implement tracefs_instances()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 02/10] libtracefs: Implement tracefs_kprobe_raw() Steven Rostedt
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Implement tracefs_instances() that will take a regex (or NULL for all) and
return a list of instances in the system.

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

diff --git a/include/tracefs.h b/include/tracefs.h
index da8ad4189d4d..a21d2d2f22a6 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -44,6 +44,7 @@ int tracefs_instance_file_read_number(struct tracefs_instance *instance,
 int tracefs_instance_file_open(struct tracefs_instance *instance,
 			       const char *file, int mode);
 int tracefs_instances_walk(int (*callback)(const char *, void *), void *context);
+char **tracefs_instances(const char *regex);
 
 bool tracefs_instance_exists(const char *name);
 bool tracefs_file_exists(struct tracefs_instance *instance, const char *name);
diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
index 2aeb529903bd..ba7667349be2 100644
--- a/src/tracefs-instance.c
+++ b/src/tracefs-instance.c
@@ -14,6 +14,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <regex.h>
 #include <limits.h>
 #include <pthread.h>
 #include "tracefs.h"
@@ -630,6 +631,81 @@ out:
 	return fret;
 }
 
+static inline bool match(const char *str, regex_t *re)
+{
+	if (!re)
+		return true;
+	return regexec(re, str, 0, NULL, 0) == 0;
+}
+
+struct instance_list {
+	regex_t		*re;
+	char		**list;
+	int		size;
+	int		failed;
+};
+
+static int build_list(const char *name, void *data)
+{
+	struct instance_list *list = data;
+	char **instances;
+	int ret = -1;
+
+	if (!match(name, list->re))
+		return 0;
+
+	instances = realloc(list->list, sizeof(*instances) * (list->size + 2));
+	if (!instances)
+		goto out;
+
+	list->list = instances;
+	instances[list->size] = strdup(name);
+	if (!instances[list->size])
+		goto out;
+
+	list->size++;
+	instances[list->size] = NULL;
+	ret = 0;
+
+ out:
+	list->failed = ret;
+	return ret;
+}
+
+/**
+ * tracefs_instances - return a list of instance names
+ * @regex: A regex of instances to filter on (NULL to match all)
+ *
+ * Returns a list of names of existing instances, that must be
+ * freed with tracefs_list_free(). Note, if there are no matches
+ * then an empty list will be returned (not NULL).
+ * NULL on error.
+ */
+char **tracefs_instances(const char *regex)
+{
+	struct instance_list list = { .re = NULL, .list = NULL };
+	regex_t re;
+	int ret;
+
+	if (regex) {
+		ret = regcomp(&re, regex, REG_ICASE|REG_NOSUB);
+		if (ret < 0)
+			return NULL;
+		list.re = &re;
+	}
+
+	ret = tracefs_instances_walk(build_list, &list);
+	if (ret < 0 || list.failed) {
+		tracefs_list_free(list.list);
+		list.list = NULL;
+	} else {
+		/* No matches should produce an empty list */
+		if (!list.list)
+			list.list = calloc(1, sizeof(*list.list));
+	}
+	return list.list;
+}
+
 /**
  * tracefs_get_clock - Get the current trace clock
  * @instance: ftrace instance, can be NULL for the top instance
-- 
2.30.2


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

* [PATCH v7 02/10] libtracefs: Implement tracefs_kprobe_raw()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 01/10] libtracefs: Implement tracefs_instances() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 03/10] libtracefs: Implement tracefs_kretprobe_raw() Steven Rostedt
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add a function to facilitate creating a kprobe event. It has the "raw" in
its name because it still requires knowing the format of the kprobe. But
does handle the kprobe naming better and writing to the kprobe_event file.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h     |  4 +++
 src/Makefile          |  1 +
 src/tracefs-kprobes.c | 72 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
 create mode 100644 src/tracefs-kprobes.c

diff --git a/include/tracefs.h b/include/tracefs.h
index a21d2d2f22a6..bc504bcb0188 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -214,4 +214,8 @@ int tracefs_tracer_clear(struct tracefs_instance *instance);
 ssize_t tracefs_trace_pipe_stream(int fd, struct tracefs_instance *instance, int flags);
 ssize_t tracefs_trace_pipe_print(struct tracefs_instance *instance, int flags);
 void tracefs_trace_pipe_stop(struct tracefs_instance *instance);
+
+int tracefs_kprobe_raw(const char *system, const char *event,
+		       const char *addr, const char *format);
+
 #endif /* _TRACE_FS_H */
diff --git a/src/Makefile b/src/Makefile
index b4cff07efc50..0697a047f4bc 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,6 +8,7 @@ OBJS += tracefs-instance.o
 OBJS += tracefs-events.o
 OBJS += tracefs-tools.o
 OBJS += tracefs-marker.o
+OBJS += tracefs-kprobes.o
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
new file mode 100644
index 000000000000..d50191d55181
--- /dev/null
+++ b/src/tracefs-kprobes.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define KPROBE_EVENTS "kprobe_events"
+
+/**
+ * tracefs_kprobe_raw - Create a kprobe using raw format
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The raw format string to define the probe.
+ *
+ * Create a kprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ *
+ * Return 0 on success, or -1 on error.
+ *   If the syntex of @format was incorrect, running
+ *   tracefs_error_last(NULL) may show what went wrong.
+ *
+ * errno will be set to EBADMSG if addr or format is NULL.
+ */
+int tracefs_kprobe_raw(const char *system, const char *event,
+		       const char *addr, const char *format)
+{
+	char *str;
+	int ret;
+
+	if (!tracefs_file_exists(NULL, KPROBE_EVENTS))
+		return -1;
+
+	errno = EBADMSG;
+	if (!addr || !format)
+		return -1;
+
+	if (!event)
+		event = addr;
+
+	if (system)
+		ret = asprintf(&str, "p:%s/%s %s %s\n",
+			       system, event, addr, format);
+	else
+		ret = asprintf(&str, "p:%s %s %s\n",
+			       event, addr, format);
+
+	if (ret < 0)
+		return -1;
+
+	ret = tracefs_instance_file_append(NULL, KPROBE_EVENTS, str);
+	free(str);
+
+	return ret < 0 ? ret : 0;
+}
-- 
2.30.2


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

* [PATCH v7 03/10] libtracefs: Implement tracefs_kretprobe_raw()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 01/10] libtracefs: Implement tracefs_instances() Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 02/10] libtracefs: Implement tracefs_kprobe_raw() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 04/10] libtracefs: Implement tracefs_get_kprobes() Steven Rostedt
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add an interface that is similar to tracefs_kprobe_raw() but creates a
"kretprobe". The difference between a kprobe and a kretprobe is that a
kretprobe comes at the end of a function.

See Documentation/trace/kprobetrace.rst in the Linux kernel source code
for more information. Note, kprobes are started with "p" and kretprobe
lines start with an "r".

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

diff --git a/include/tracefs.h b/include/tracefs.h
index bc504bcb0188..64164c8c2b20 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -217,5 +217,6 @@ void tracefs_trace_pipe_stop(struct tracefs_instance *instance);
 
 int tracefs_kprobe_raw(const char *system, const char *event,
 		       const char *addr, const char *format);
-
+int tracefs_kretprobe_raw(const char *system, const char *event,
+			  const char *addr, const char *format);
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index d50191d55181..4970620b28f2 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -19,28 +19,9 @@
 
 #define KPROBE_EVENTS "kprobe_events"
 
-/**
- * tracefs_kprobe_raw - Create a kprobe using raw format
- * @system: The system name (NULL for the default kprobes)
- * @event: The event to create (NULL to use @addr for the event)
- * @addr: The function and offset (or address) to insert the probe
- * @format: The raw format string to define the probe.
- *
- * Create a kprobe that will be in the @system group (or kprobes if
- * @system is NULL). Have the name of @event (or @addr if @event is
- * NULL). Will be inserted to @addr (function name, with or without
- * offset, or a address). And the @format will define the raw format
- * of the kprobe. See the Linux documentation file under:
- * Documentation/trace/kprobetrace.rst
- *
- * Return 0 on success, or -1 on error.
- *   If the syntex of @format was incorrect, running
- *   tracefs_error_last(NULL) may show what went wrong.
- *
- * errno will be set to EBADMSG if addr or format is NULL.
- */
-int tracefs_kprobe_raw(const char *system, const char *event,
-		       const char *addr, const char *format)
+static int insert_kprobe(const char *type, const char *system,
+			 const char *event, const char *addr,
+			 const char *format)
 {
 	char *str;
 	int ret;
@@ -56,11 +37,11 @@ int tracefs_kprobe_raw(const char *system, const char *event,
 		event = addr;
 
 	if (system)
-		ret = asprintf(&str, "p:%s/%s %s %s\n",
-			       system, event, addr, format);
+		ret = asprintf(&str, "%s:%s/%s %s %s\n",
+			       type, system, event, addr, format);
 	else
-		ret = asprintf(&str, "p:%s %s %s\n",
-			       event, addr, format);
+		ret = asprintf(&str, "%s:%s %s %s\n",
+			       type, event, addr, format);
 
 	if (ret < 0)
 		return -1;
@@ -70,3 +51,55 @@ int tracefs_kprobe_raw(const char *system, const char *event,
 
 	return ret < 0 ? ret : 0;
 }
+
+/**
+ * tracefs_kprobe_raw - Create a kprobe using raw format
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The raw format string to define the probe.
+ *
+ * Create a kprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ *
+ * Return 0 on success, or -1 on error.
+ *   If the syntex of @format was incorrect, running
+ *   tracefs_error_last(NULL) may show what went wrong.
+ *
+ * errno will be set to EBADMSG if addr or format is NULL.
+ */
+int tracefs_kprobe_raw(const char *system, const char *event,
+		       const char *addr, const char *format)
+{
+	return insert_kprobe("p", system, event, addr, format);
+}
+
+/**
+ * tracefs_kretprobe_raw - Create a kretprobe using raw format
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the retprobe
+ * @format: The raw format string to define the retprobe.
+ *
+ * Create a kretprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ *
+ * Return 0 on success, or -1 on error.
+ *   If the syntex of @format was incorrect, running
+ *   tracefs_error_last(NULL) may show what went wrong.
+ *
+ * errno will be set to EBADMSG if addr or format is NULL.
+ */
+int tracefs_kretprobe_raw(const char *system, const char *event,
+			  const char *addr, const char *format)
+{
+	return insert_kprobe("r", system, event, addr, format);
+}
-- 
2.30.2


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

* [PATCH v7 04/10] libtracefs: Implement tracefs_get_kprobes()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (2 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 03/10] libtracefs: Implement tracefs_kretprobe_raw() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 05/10] libtracefs: Add helper function to parse kprobes Steven Rostedt
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add the function tracefs_get_kprobes() that returns a list of kprobes that
are registered. The list contains strings that are of the format
"group/event" (i.e. "kprobes/open"). The last element in the list is a
NULL pointer. In the case that there are no kprobes, the list will return
a single entry of a NULL pointer.

tracefs_get_kprobes() takes a type parameter to determine which kprobes to
return:

  TRACEFS_ALL_KPROBES	- return all that are found
  TRACEFS_KPROBE	- return only normal kprobes ("p:")
  TRACEFS_KRETPROBE	- return only kretprobes ("r:")

NULL is returned in the case of error (mainly a memory issue, or bad
parsing).

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h     |  7 ++++
 src/tracefs-kprobes.c | 93 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 100 insertions(+)

diff --git a/include/tracefs.h b/include/tracefs.h
index 64164c8c2b20..e8a2008d9714 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -215,8 +215,15 @@ ssize_t tracefs_trace_pipe_stream(int fd, struct tracefs_instance *instance, int
 ssize_t tracefs_trace_pipe_print(struct tracefs_instance *instance, int flags);
 void tracefs_trace_pipe_stop(struct tracefs_instance *instance);
 
+enum tracefs_kprobe_type {
+	TRACEFS_ALL_KPROBES,
+	TRACEFS_KPROBE,
+	TRACEFS_KRETPROBE,
+};
+
 int tracefs_kprobe_raw(const char *system, const char *event,
 		       const char *addr, const char *format);
 int tracefs_kretprobe_raw(const char *system, const char *event,
 			  const char *addr, const char *format);
+char **tracefs_get_kprobes(enum tracefs_kprobe_type type);
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index 4970620b28f2..3bba3683f29b 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -103,3 +103,96 @@ int tracefs_kretprobe_raw(const char *system, const char *event,
 {
 	return insert_kprobe("r", system, event, addr, format);
 }
+
+/**
+ * tracefs_get_kprobes - return a list kprobes (by group/event name)
+ * @type: The type of kprobes to return.
+ *
+ * If @type is TRACEFS_ALL_KPROBES all kprobes in the kprobe_events
+ * are returned. Otherwise if it is TRACEFS_KPROBE, then only
+ * normal kprobes (p:) are returned, or if type is TRACEFS_KRETPROBE
+ * then only kretprobes (r:) are returned.
+ *
+ * Returns a list of strings that contain the kprobes that exist
+ * in the kprobe_events files. The strings returned are in the
+ * "group/event" format.
+ * The list must be freed with tracefs_list_free().
+ * If there are no kprobes, a list is still returned, but it contains
+ * only a NULL pointer.
+ * On error, NULL is returned.
+ */
+char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
+{
+	char **list = NULL;
+	char *content;
+	char *saveptr;
+	char *event;
+	char *p;
+	int cnt = 0;
+
+	errno = 0;
+	content = tracefs_instance_file_read(NULL, KPROBE_EVENTS, NULL);
+	if (!content) {
+		if (errno)
+			return NULL;
+		/* content is NULL on empty file, return an empty list */
+		list = calloc(1, sizeof(*list));
+		return list;
+	}
+
+	p = strtok_r(content, ":", &saveptr);
+
+	while (p) {
+		char **tmp;
+
+		if (type != TRACEFS_ALL_KPROBES) {
+			switch (*p) {
+			case 'p':
+				if (type != TRACEFS_KPROBE)
+					goto next;
+				break;
+			case 'r':
+				if (type != TRACEFS_KRETPROBE)
+					goto next;
+				break;
+			default:
+				goto next;
+			}
+		}
+
+		/* Failed parsing always return a failure */
+		p = strtok_r(NULL, " ", &saveptr);
+		if (!p)
+			break;
+
+		event = strdup(p);
+		if (!event)
+			goto fail;
+
+		tmp = realloc(list, sizeof(*list) * (cnt + 2));
+		if (!tmp)
+			goto fail;
+
+		list = tmp;
+		list[cnt++] = event;
+		list[cnt] = NULL;
+ next:
+		p = strtok_r(NULL, "\n", &saveptr);
+		/* Could be end of content */
+		if (!p)
+			break;
+
+		/* p is NULL on end of content */
+		p = strtok_r(NULL, ":", &saveptr);
+	}
+
+	if (!list)
+		list = calloc(1, sizeof(*list));
+ out:
+	free(content);
+	return list;
+ fail:
+	free(list);
+	list = NULL;
+	goto out;
+}
-- 
2.30.2


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

* [PATCH v7 05/10] libtracefs: Add helper function to parse kprobes
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (3 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 04/10] libtracefs: Implement tracefs_get_kprobes() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 06/10] libtracefs: Implement tracefs_kprobe_info() Steven Rostedt
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add a helper function to parse kprobes and use that for
tracefs_get_kprobes(). This will allow other functions to easily parse the
kprobe_events file without having to duplicate the parsing code.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 src/tracefs-kprobes.c | 76 +++++++++++++++++++++++++++++++++----------
 1 file changed, 59 insertions(+), 17 deletions(-)

diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index 3bba3683f29b..713715e96f4f 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -104,6 +104,58 @@ int tracefs_kretprobe_raw(const char *system, const char *event,
 	return insert_kprobe("r", system, event, addr, format);
 }
 
+/*
+ * Helper function to parse kprobes.
+ * @content: The content of kprobe_events on the first iteration.
+ *           NULL on next iterations.
+ * @saveptr: Same as saveptr for strtok_r
+ * @type: Where to store the type (before ':')
+ * @system: Store the system of the kprobe (NULL to have event contain
+ *          both system and event, as in "kprobes/myprobe").
+ * @event: Where to store the event.
+ * @addr: Where to store the addr (may be NULL to ignore)
+ * @format: Where to store the format (may be NULL to ignore)
+ */
+static int parse_kprobe(char *content, char **saveptr,
+			char **type, char **system, char **event,
+			char **addr, char **format)
+{
+	char *p;
+
+	p = strtok_r(content, ":", saveptr);
+	if (!p)
+		return 1; /* eof */
+	*type = p;
+
+	if (system) {
+		p = strtok_r(NULL, "/", saveptr);
+		if (!p)
+			return -1;
+		*system = p;
+	}
+
+	p = strtok_r(NULL, " ", saveptr);
+	if (!p)
+		return -1;
+	*event = p;
+
+	if (addr || format) {
+		p = strtok_r(NULL, " ", saveptr);
+		if (!p)
+			return -1;
+		if (addr)
+			*addr = p;
+	}
+
+	p = strtok_r(NULL, "\n", saveptr);
+	if (!p)
+		return -1;
+	if (format)
+		*format = p;
+
+	return 0;
+}
+
 /**
  * tracefs_get_kprobes - return a list kprobes (by group/event name)
  * @type: The type of kprobes to return.
@@ -127,8 +179,9 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
 	char *content;
 	char *saveptr;
 	char *event;
-	char *p;
+	char *ktype;
 	int cnt = 0;
+	int ret;
 
 	errno = 0;
 	content = tracefs_instance_file_read(NULL, KPROBE_EVENTS, NULL);
@@ -140,13 +193,13 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
 		return list;
 	}
 
-	p = strtok_r(content, ":", &saveptr);
+	ret = parse_kprobe(content, &saveptr, &ktype, NULL, &event, NULL, NULL);
 
-	while (p) {
+	while (!ret) {
 		char **tmp;
 
 		if (type != TRACEFS_ALL_KPROBES) {
-			switch (*p) {
+			switch (*ktype) {
 			case 'p':
 				if (type != TRACEFS_KPROBE)
 					goto next;
@@ -160,12 +213,7 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
 			}
 		}
 
-		/* Failed parsing always return a failure */
-		p = strtok_r(NULL, " ", &saveptr);
-		if (!p)
-			break;
-
-		event = strdup(p);
+		event = strdup(event);
 		if (!event)
 			goto fail;
 
@@ -177,13 +225,7 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
 		list[cnt++] = event;
 		list[cnt] = NULL;
  next:
-		p = strtok_r(NULL, "\n", &saveptr);
-		/* Could be end of content */
-		if (!p)
-			break;
-
-		/* p is NULL on end of content */
-		p = strtok_r(NULL, ":", &saveptr);
+		ret = parse_kprobe(NULL, &saveptr, &ktype, NULL, &event, NULL, NULL);
 	}
 
 	if (!list)
-- 
2.30.2


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

* [PATCH v7 06/10] libtracefs: Implement tracefs_kprobe_info()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (4 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 05/10] libtracefs: Add helper function to parse kprobes Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 07/10] libtracefs: Implement tracefs_kprobe_clear_all() to remove all kprobes Steven Rostedt
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add tracefs_kprobe_info() that returns the type of a given probe, and may
also return the content of the parsed kprobe_events file.

If @type is non NULL, it will hold the content of the kprobe before the
":'.

If @addr is non NULL, it will hold the content of the kprobe's address or
function that it is attached to.

If @format is non NULL, it will hold the format string of the kprobe.

It returns:

    TRACEFS_ALL_KPROBES if an error occurs or the kprobe is not found,
             or the probe is of an unknown type.
             Having @type set, can help debug an unknown type.
    TRACEFS_KPROBE if the type of kprobe found is a normal kprobe.
    TRACEFS_KRETPROBE if the type of kprobe found is a kretprobe.

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

diff --git a/include/tracefs.h b/include/tracefs.h
index e8a2008d9714..f0ab7c96e4ab 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -226,4 +226,6 @@ int tracefs_kprobe_raw(const char *system, const char *event,
 int tracefs_kretprobe_raw(const char *system, const char *event,
 			  const char *addr, const char *format);
 char **tracefs_get_kprobes(enum tracefs_kprobe_type type);
+enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *event,
+					     char **type, char **addr, char **format);
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index 713715e96f4f..5442b6c17638 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -18,6 +18,7 @@
 #include "tracefs-local.h"
 
 #define KPROBE_EVENTS "kprobe_events"
+#define KPROBE_DEFAULT_GROUP "kprobes"
 
 static int insert_kprobe(const char *type, const char *system,
 			 const char *event, const char *addr,
@@ -238,3 +239,74 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type)
 	list = NULL;
 	goto out;
 }
+
+/**
+ * tracefs_kprobe_info - return the type of kprobe specified.
+ * @group: The group the kprobe is in (NULL for the default "kprobes")
+ * @event: The name of the kprobe to find.
+ * @type: String to return kprobe type (before ':') NULL to ignore.
+ * @addr: String to return address kprobe is attached to. NULL to ignore.
+ * @format: String to return kprobe format. NULL to ignore.
+ *
+ * If @type, @addr, or @format is non NULL, then the returned string
+ * must be freed with free(). They will also be set to NULL, and
+ * even on error, they may contain strings to be freed. If they are
+ * not NULL, then they still need to be freed.
+ *
+ * Returns TRACEFS_ALL_KPROBES if an error occurs or the kprobe is not found,
+ *            or the probe is of an unknown type.
+ * TRACEFS_KPROBE if the type of kprobe found is a normal kprobe.
+ * TRACEFS_KRETPROBE if the type of kprobe found is a kretprobe.
+ */
+enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *event,
+					     char **type, char **addr, char **format)
+{
+	enum tracefs_kprobe_type rtype = TRACEFS_ALL_KPROBES;
+	char *saveptr;
+	char *content;
+	char *system;
+	char *probe;
+	char *ktype;
+	char *kaddr;
+	char *kfmt;
+	int ret;
+
+	if (!group)
+		group = KPROBE_DEFAULT_GROUP;
+
+	if (type)
+		*type = NULL;
+	if (addr)
+		*addr = NULL;
+	if (format)
+		*format = NULL;
+
+	content = tracefs_instance_file_read(NULL, KPROBE_EVENTS, NULL);
+	if (!content)
+		return rtype;
+
+	ret = parse_kprobe(content, &saveptr, &ktype, &system, &probe,
+			   &kaddr, &kfmt);
+
+	while (!ret) {
+
+		if (!strcmp(system, group) && !strcmp(probe, event)) {
+			if (type)
+				*type = strdup(ktype);
+			if (addr)
+				*addr = strdup(kaddr);
+			if (format)
+				*format = strdup(kfmt);
+
+			switch (*ktype) {
+			case 'p': rtype = TRACEFS_KPROBE; break;
+			case 'r': rtype = TRACEFS_KRETPROBE; break;
+			}
+			break;
+		}
+		ret = parse_kprobe(NULL, &saveptr, &ktype, &system, &probe,
+				   &kaddr, &kfmt);
+	}
+	free(content);
+	return rtype;
+}
-- 
2.30.2


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

* [PATCH v7 07/10] libtracefs: Implement tracefs_kprobe_clear_all() to remove all kprobes
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (5 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 06/10] libtracefs: Implement tracefs_kprobe_info() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 08/10] libtracefs: Implement tracefs_kprobe_clear_probe() Steven Rostedt
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

A call to tracefs_kprobe_clear_all() will attempt to disable all kprobes. If
any kprobe is set, and the @force parameter is set, it will fail with
errno set to EBUSY. If @force is set, then it will attempt to disable all
the defined kprobe events and then clear it.

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

diff --git a/include/tracefs.h b/include/tracefs.h
index f0ab7c96e4ab..198517f23eca 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -228,4 +228,5 @@ int tracefs_kretprobe_raw(const char *system, const char *event,
 char **tracefs_get_kprobes(enum tracefs_kprobe_type type);
 enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *event,
 					     char **type, char **addr, char **format);
+int tracefs_kprobe_clear_all(bool force);
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index 5442b6c17638..bafc15a4ce5d 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -310,3 +310,101 @@ enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *even
 	free(content);
 	return rtype;
 }
+
+static void disable_events(const char *system, const char *event,
+			   char **list)
+{
+	struct tracefs_instance *instance;
+	int i;
+
+	/*
+	 * Note, this will not fail even on error.
+	 * That is because even if something fails, it may still
+	 * work enough to clear the kprobes. If that's the case
+	 * the clearing after the loop will succeed and the function
+	 * is a success, even though other parts had failed. If
+	 * one of the kprobe events is enabled in one of the
+	 * instances that fail, then the clearing will fail too
+	 * and the function will return an error.
+	 */
+
+	tracefs_event_disable(NULL, system, event);
+	/* No need to test results */
+
+	if (!list)
+		return;
+
+	for (i = 0; list[i]; i++) {
+		instance = tracefs_instance_alloc(NULL, list[i]);
+		/* If this fails, try the next one */
+		if (!instance)
+			continue;
+		tracefs_event_disable(instance, system, event);
+		tracefs_instance_free(instance);
+	}
+	return;
+}
+
+/**
+ * tracefs_kprobe_clear_all - clear kprobe events
+ * @force: Will attempt to disable all kprobe events and clear them
+ *
+ * Will remove all defined kprobe events. If any of them are enabled,
+ * and @force is not set, then it will error with -1 and errno to be
+ * EBUSY. If @force is set, then it will attempt to disable all the kprobe
+ * events in all instances, and try again.
+ *
+ * Returns zero on success, -1 otherwise.
+ */
+int tracefs_kprobe_clear_all(bool force)
+{
+	char **instance_list;
+	char **kprobe_list;
+	char *saveptr;
+	char *system;
+	char *kprobe;
+	char *event;
+	int ret;
+	int i;
+
+	ret = tracefs_instance_file_clear(NULL, KPROBE_EVENTS);
+	if (!ret)
+		return 0;
+
+	if (!force)
+		return -1;
+
+	kprobe_list = tracefs_get_kprobes(TRACEFS_ALL_KPROBES);
+	if (!kprobe_list)
+		return -1;
+
+	instance_list = tracefs_instances(NULL);
+	/*
+	 * Even if the above failed and instance_list is NULL,
+	 * keep going, as the enabled event may simply be in the
+	 * top level.
+	 */
+
+	for (i = 0; kprobe_list[i]; i++) {
+		kprobe = kprobe_list[i];
+
+		system = strtok_r(kprobe, "/", &saveptr);
+		if (!system)
+			goto out;
+
+		event = strtok_r(NULL," ", &saveptr);
+		if (!event)
+			goto out;
+
+		disable_events(system, event, instance_list);
+
+		ret = tracefs_instance_file_clear(NULL, KPROBE_EVENTS);
+		/* On success stop the loop */
+		if (!ret)
+			goto out;
+	}
+ out:
+	tracefs_list_free(instance_list);
+	tracefs_list_free(kprobe_list);
+	return ret;
+}
-- 
2.30.2


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

* [PATCH v7 08/10] libtracefs: Implement tracefs_kprobe_clear_probe()
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (6 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 07/10] libtracefs: Implement tracefs_kprobe_clear_all() to remove all kprobes Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 09/10] libtracefs: Add man pages for kprobe functions Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 10/10] libtracefs: Update the unit tests to use the kprobe API instead Steven Rostedt
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add the function tracefs_kprobe_clear_probe() that will remove a single
kprobe. If the @force parameter is set, it will disable that probe in all
instances (including the top level instance) before removing it.

If the @event parameter is NULL, then it will clear all events that are
defined by the @system parameter. If the @system parameter is NULL,
then it will use the default "kprobes" group.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 include/tracefs.h     |   1 +
 src/tracefs-kprobes.c | 123 +++++++++++++++++++++++++++++++++---------
 2 files changed, 100 insertions(+), 24 deletions(-)

diff --git a/include/tracefs.h b/include/tracefs.h
index 198517f23eca..1c8703ae7e26 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -229,4 +229,5 @@ char **tracefs_get_kprobes(enum tracefs_kprobe_type type);
 enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *event,
 					     char **type, char **addr, char **format);
 int tracefs_kprobe_clear_all(bool force);
+int tracefs_kprobe_clear_probe(const char *system, const char *event, bool force);
 #endif /* _TRACE_FS_H */
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index bafc15a4ce5d..2bc589a82627 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -345,18 +345,17 @@ static void disable_events(const char *system, const char *event,
 	return;
 }
 
-/**
- * tracefs_kprobe_clear_all - clear kprobe events
- * @force: Will attempt to disable all kprobe events and clear them
- *
- * Will remove all defined kprobe events. If any of them are enabled,
- * and @force is not set, then it will error with -1 and errno to be
- * EBUSY. If @force is set, then it will attempt to disable all the kprobe
- * events in all instances, and try again.
- *
- * Returns zero on success, -1 otherwise.
- */
-int tracefs_kprobe_clear_all(bool force)
+static int clear_kprobe(const char *system, const char *event)
+{
+	/* '-' + ':' + '/' + '\n' + '\0' = 5 bytes */
+	int len = strlen(system) + strlen(event) + 5;
+	char content[len];
+
+	sprintf(content, "-:%s/%s", system, event);
+	return tracefs_instance_file_append(NULL, KPROBE_EVENTS, content);
+}
+
+static int kprobe_clear_probes(const char *group, bool force)
 {
 	char **instance_list;
 	char **kprobe_list;
@@ -367,13 +366,6 @@ int tracefs_kprobe_clear_all(bool force)
 	int ret;
 	int i;
 
-	ret = tracefs_instance_file_clear(NULL, KPROBE_EVENTS);
-	if (!ret)
-		return 0;
-
-	if (!force)
-		return -1;
-
 	kprobe_list = tracefs_get_kprobes(TRACEFS_ALL_KPROBES);
 	if (!kprobe_list)
 		return -1;
@@ -385,6 +377,13 @@ int tracefs_kprobe_clear_all(bool force)
 	 * top level.
 	 */
 
+	/*
+	 * If a system is defined, the default is to pass unless
+	 * an event fails to be removed. If a system is not defined,
+	 * the default is to fail, unless all are removed.
+	 */
+	ret = group ? 0 : -1;
+
 	for (i = 0; kprobe_list[i]; i++) {
 		kprobe = kprobe_list[i];
 
@@ -396,15 +395,91 @@ int tracefs_kprobe_clear_all(bool force)
 		if (!event)
 			goto out;
 
-		disable_events(system, event, instance_list);
+		/* Skip if this does not match a given system */
+		if (group && strcmp(system, group) != 0)
+			continue;
 
-		ret = tracefs_instance_file_clear(NULL, KPROBE_EVENTS);
-		/* On success stop the loop */
-		if (!ret)
-			goto out;
+		if (force)
+			disable_events(system, event, instance_list);
+
+		if (group) {
+			ret = clear_kprobe(system, event);
+			if (ret < 0)
+				goto out;
+		} else {
+			ret = tracefs_instance_file_clear(NULL, KPROBE_EVENTS);
+			/* On success stop the loop */
+			if (!ret)
+				goto out;
+		}
+
+		/* Set the default for whether a system is defined or not */
+		ret = group ? 0 : -1;
 	}
  out:
 	tracefs_list_free(instance_list);
 	tracefs_list_free(kprobe_list);
 	return ret;
 }
+
+/**
+ * tracefs_kprobe_clear_all - clear kprobe events
+ * @force: Will attempt to disable all kprobe events and clear them
+ *
+ * Will remove all defined kprobe events. If any of them are enabled,
+ * and @force is not set, then it will error with -1 and errno to be
+ * EBUSY. If @force is set, then it will attempt to disable all the kprobe
+ * events in all instances, and try again.
+ *
+ * Returns zero on success, -1 otherwise.
+ */
+int tracefs_kprobe_clear_all(bool force)
+{
+	if (tracefs_instance_file_clear(NULL, KPROBE_EVENTS) == 0)
+		return 0;
+
+	if (!force)
+		return -1;
+
+	/* Attempt to disable all kprobe events */
+	return kprobe_clear_probes(NULL, force);
+}
+
+/**
+ * tracefs_kprobe_clear_all - clear kprobe events
+ * @system: System to clear (NULL means default)
+ * @event: Name of probe to clear in system (NULL for all probes in system)
+ * @force: Will attempt to disable all kprobe events and clear them
+ *
+ * Will remove the kprobes that match the @system and @event. If @system
+ * is NULL, then "kprobes" is used and will ignore all other system
+ * groups of kprobes. The @event is NULL then all events under the given
+ * @system are removed, otherwise only the event that matches.
+ *
+ * Returns zero on success, -1 otherwise.
+ */
+int tracefs_kprobe_clear_probe(const char *system, const char *event, bool force)
+{
+	char **instance_list;
+	int ret;
+
+	if (!system)
+		system = "kprobes";
+
+	if (!event)
+		return kprobe_clear_probes(system, force);
+
+	/*
+	 * Since we know we are disabling a specific event, try
+	 * to disable it first before clearing it.
+	 */
+	if (force) {
+		instance_list = tracefs_instances(NULL);
+		disable_events(system, event, instance_list);
+		tracefs_list_free(instance_list);
+	}
+
+	ret = clear_kprobe(system, event);
+
+	return ret < 0 ? -1 : 0;
+}
-- 
2.30.2


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

* [PATCH v7 09/10] libtracefs: Add man pages for kprobe functions
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (7 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 08/10] libtracefs: Implement tracefs_kprobe_clear_probe() Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  2021-07-02 20:17 ` [PATCH v7 10/10] libtracefs: Update the unit tests to use the kprobe API instead Steven Rostedt
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Add man pages that describe the following functions:

    tracefs_kprobe_raw
    tracefs_kretprobe_raw
    tracefs_get_kprobes
    tracefs_kprobe_info
    tracefs_kprobe_clear_all
    tracefs_kprobe_clear_probe

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

diff --git a/Documentation/libtracefs-kprobes.txt b/Documentation/libtracefs-kprobes.txt
new file mode 100644
index 000000000000..c10576e2897e
--- /dev/null
+++ b/Documentation/libtracefs-kprobes.txt
@@ -0,0 +1,297 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_kprobe_raw, tracefs_kretprobe_raw, tracefs_get_kprobes, tracefs_kprobe_info, tracefs_kprobe_clear_all, tracefs_kprobe_clear_probe - Create, list, and destroy kprobes
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int tracefs_kprobe_raw(const char pass:[*]system, const char pass:[*]event, const char pass:[*]addr, const char pass:[*]format);
+int tracefs_kretprobe_raw(const char pass:[*]system, const char pass:[*]event, const char pass:[*]addr, const char pass:[*]format);
+char pass:[*]pass:[*]tracefs_get_kprobes(enum tracefs_kprobe_type type);
+enum tracefs_kprobe_type tracefs_kprobe_info(const char pass:[*]group, const char pass:[*]event,
+					     char pass:[*]pass:[*]type, char pass:[*]pass:[*]addr, char pass:[*]pass:[*]format);
+enum tracefs_kprobe_type tracefs_kprobe_type(const char pass:[*]group, const char pass:[*]event)
+int tracefs_kprobe_clear_all(bool force);
+int tracefs_kprobe_clear_probe(const char pass:[*]system, const char pass:[*]event, bool force);
+--
+
+DESCRIPTION
+-----------
+*tracefs_kprobe_raw*() will create a kprobe event. If _system_ is NULL, then
+the default "kprobes" is used for the group (event system). Otherwise if _system_
+is specified then the kprobe will be created under the group by that name. The
+_event_ is the name of the kprobe event to create. The _addr_ can be a function,
+a function and offset, or a kernel address. This is where the location of the
+kprobe will be inserted in the kernel. The _format_ is the kprobe format as
+specified as FETCHARGS in the Linux kernel source in the Documentation/trace/kprobetrace.rst
+document.
+
+*tracefs_kretprobe_raw*() is the same as *tracefs_kprobe_raw()*, except that it
+creates a kretprobe instead of a kprobe. The difference is also described
+in the Linux kernel source in the Documentation/trace/kprobetrace.rst file.
+
+*tracefs_get_kprobes*() returns an array of strings (char pass:[*]) that contain
+the registered kprobes and kretprobes depending on the given _type_. If _type_ is
+TRACEFS_ALL_KPROBES, then all kprobes found are returned. If _type_ is
+TRACEFS_KPROBE, then only normal kprobes are returned. If _type_ is
+TRACEFS_KRETPROBE, then only kretprobes are returned.
+The names are in the "system/event" format.
+That is, one string holds both the kprobe's name as well as the group it is
+defined under. These strings are allocated and may be modified with the
+*strtok*(3) and *strtok_r*(3) functions. The string returned must be freed with
+*tracefs_list_free*(3).
+
+*tracefs_kprobe_info*() returns the type of the given kprobe. If _group_ is
+NULL, then the default "kprobes" is used. If _type_ is non NULL, then it will
+hold an allocated string that holds the type portion of the kprobe in the
+kprobe_events file (the content before the ":"). If _addr_ is non NULL, it will
+hold the address or function that the kprobe is attached to. If _format_ is non
+NULL, it will hold the format string of the kprobe. Note, that the content in
+_type_, _addr_, and _format_ must be freed with free(3) if they are set. Even
+in the case of an error, as they may hold information of what caused the error.
+
+*tracefs_kprobe_clear_all*() will try to remove all kprobes that have been
+registered. If the @force flag is set, it will then disable those kprobe events
+if they are enabled and then try to clear the kprobes.
+
+*tracefs_kprobe_clear_probe*() will try to clear specified kprobes. If _system_
+is NULL, then it will only clear the default kprobes under the "kprobes" group.
+If _event_ is NULL, it will clear all kprobes under the given _system_. If the
+_force_ flag is set, then it will disable the given kprobe events before clearing
+them.
+
+RETURN VALUE
+------------
+
+*tracefs_kprobe_raw*(), *tracefs_kretprobe_raw*(), *tracefs_kprobe_clear_all*(),
+and *tracefs_kprobe_clear_probe*() return 0 on success, or -1 on error.
+
+If a parsing error occurs on *tracefs_kprobe_raw*() or *tracefs_kretprobe_raw*()
+then *tracefs_error_last*(3) may be used to retrieve the error message explaining
+the parsing issue.
+
+*tracefs_get_kprobes*() returns an allocate string list of allocated strings
+on success that must be freed with *tracefs_list_free*(3) and returns
+NULL on error.
+
+*tracefs_kprobe_info*() returns the type of the given kprobe. It returns
+TRACEFS_KPROBE for normal kprobes, TRACEFS_KRETPROBE for kretprobes, and
+on error, or if the kprobe is not found TRACEFS_ALL_KPROBES is returned.
+If _type_, _addr_, or _format_ are non NULL, they will contain allocated
+strings that must be freed by free(3) even in the case of error.
+
+ERRORS
+------
+The following errors are for all the above calls:
+
+*EPERM* Not run as root user
+
+*ENODEV* Kprobe events are not configured for the running kernel.
+
+*ENOMEM* Memory allocation error.
+
+*tracefs_kprobe_raw*(), *tracefs_kretprobe_raw*() can fail with the following errors:
+
+*EBADMSG* Either _addr_ or _format_ are NULL.
+
+*EINVAL*  Most likely a parsing error occurred (use *tracefs_error_last*(3) to possibly
+          see what that error was).
+
+Other errors may also happen caused by internal system calls.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <tracefs/tracefs.h>
+
+static struct tep_event *open_event;
+static struct tep_format_field *file_field;
+
+static struct tep_event *openret_event;
+static struct tep_format_field *ret_field;
+
+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: ", TEP_PRINT_PID, TEP_PRINT_COMM);
+
+	if (event->id == open_event->id) {
+		trace_seq_puts(&seq, "open file='");
+		tep_print_field(&seq, record->data, file_field);
+		trace_seq_puts(&seq, "'\n");
+	} else if (event->id == openret_event->id) {
+		unsigned long long ret;
+		tep_read_number_field(ret_field, record->data, &ret);
+		trace_seq_printf(&seq, "open ret=%lld\n", ret);
+	} else {
+		goto out;
+	}
+
+	trace_seq_terminate(&seq);
+	trace_seq_do_printf(&seq);
+out:
+	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 *mykprobe = "my_kprobes";
+
+enum kprobe_type {
+	KPROBE,
+	KRETPROBE,
+};
+
+static void __kprobe_create(enum kprobe_type type, const char *event,
+			    const char *addr, const char *fmt)
+{
+	char *err;
+	int r;
+
+	if (type == KPROBE)
+		r = tracefs_kprobe_raw(mykprobe, event, addr, fmt);
+	else
+		r = tracefs_kretprobe_raw(mykprobe, event, addr, fmt);
+	if (r < 0) {
+		err = tracefs_error_last(NULL);
+		perror("Failed to create kprobe:");
+		if (err && strlen(err))
+			fprintf(stderr, "%s\n", err);
+	}
+}
+
+static void kprobe_create(const char *event, const char *addr,
+			  const char *fmt)
+{
+	__kprobe_create(KPROBE, event, addr, fmt);
+}
+
+static void kretprobe_create(const char *event, const char *addr,
+			     const char *fmt)
+{
+	__kprobe_create(KRETPROBE, event, addr, fmt);
+}
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_instance *instance;
+	struct tep_handle *tep;
+	const char *sysnames[] = { mykprobe, NULL };
+	pid_t pid;
+
+	if (argc < 2) {
+		printf("usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("exec_open");
+	if (!instance) {
+		perror("creating instance");
+		exit(-1);
+	}
+
+	tracefs_kprobe_clear_probe(mykprobe, NULL, true);
+
+	kprobe_create("open", "do_sys_openat2",
+		      "file=+0($arg2):ustring flags=+0($arg3):x64 mode=+8($arg3):x64\n");
+
+	kretprobe_create("openret", "do_sys_openat2", "ret=%ax");
+
+	tep = tracefs_local_events_system(NULL, sysnames);
+	if (!tep) {
+		perror("reading events");
+		exit(-1);
+	}
+	open_event = tep_find_event_by_name(tep, mykprobe, "open");
+	file_field = tep_find_field(open_event, "file");
+
+	openret_event = tep_find_event_by_name(tep, mykprobe, "openret");
+	ret_field = tep_find_field(openret_event, "ret");
+
+	tracefs_event_enable(instance, mykprobe, NULL);
+	pid = run_exec(&argv[1], 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);
+
+	/* Will disable the events */
+	tracefs_kprobe_clear_probe(mykprobe, NULL, true);
+	tracefs_instance_destroy(instance);
+	tep_free(tep);
+
+	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>
+*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 related	[flat|nested] 11+ messages in thread

* [PATCH v7 10/10] libtracefs: Update the unit tests to use the kprobe API instead
  2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
                   ` (8 preceding siblings ...)
  2021-07-02 20:17 ` [PATCH v7 09/10] libtracefs: Add man pages for kprobe functions Steven Rostedt
@ 2021-07-02 20:17 ` Steven Rostedt
  9 siblings, 0 replies; 11+ messages in thread
From: Steven Rostedt @ 2021-07-02 20:17 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Steven Rostedt (VMware)

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

Change the unit test from accessing the kprobe_events file directly and
instead use the new kprobe API instead, and test that.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 utest/tracefs-utest.c | 195 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 166 insertions(+), 29 deletions(-)

diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index a982b1d70ef5..2a99217b938a 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -28,10 +28,21 @@
 #define TRACE_ON	"tracing_on"
 #define TRACE_CLOCK	"trace_clock"
 
-#define KPROB_EVTS	"kprobe_events"
-#define KPROBE_1	"p:mkdir do_mkdirat path=+u0($arg2):ustring"
-#define KPROBE_1_RM	"-:mkdir"
-#define KPROBE_2	"p:open do_sys_openat2 file=+u0($arg2):ustring flags=+0($arg3):x64"
+#define KPROBE_EVENTS	"kprobe_events"
+
+#define KPROBE_1_NAME	"mkdir"
+#define KPROBE_1_GROUP	"kprobes"
+#define KPROBE_1_ADDR	"do_mkdirat"
+#define KPROBE_1_FMT	"path=+u0($arg2):ustring"
+
+#define KPROBE_2_NAME	"open"
+#define KPROBE_2_GROUP	"myprobe"
+#define KPROBE_2_ADDR	"do_sys_openat2"
+#define KPROBE_2_FMT	"file=+u0($arg2):ustring flags=+0($arg3):x64"
+
+#define KRETPROBE_NAME	"retopen"
+#define KRETPROBE_ADDR	"do_sys_openat2"
+#define KRETPROBE_FMT	"ret=$retval"
 
 static struct tracefs_instance *test_instance;
 static struct tep_handle *test_tep;
@@ -374,19 +385,24 @@ static void test_instance_file(void)
 {
 	struct tracefs_instance *instance = NULL;
 	struct tracefs_instance *second = NULL;
+	enum tracefs_kprobe_type type;
 	const char *name = get_rand_str();
 	const char *inst_name = NULL;
 	const char *tdir;
 	char *inst_file;
 	char *inst_dir;
 	struct stat st;
-	char *kprobes;
+	char **kprobes;
+	char *kformat;
+	char *ktype;
+	char *kaddr;
 	char *fname;
 	char *file1;
 	char *file2;
 	char *tracer;
 	int size;
 	int ret;
+	int i;
 
 	tdir  = tracefs_tracing_dir();
 	CU_TEST(tdir != NULL);
@@ -449,31 +465,152 @@ static void test_instance_file(void)
 	free(file1);
 	free(file2);
 
-	ret = tracefs_instance_file_write(NULL, KPROB_EVTS, KPROBE_1);
-	CU_TEST(ret == strlen(KPROBE_1));
-	kprobes = tracefs_instance_file_read(NULL, KPROB_EVTS, &size);
-	CU_TEST_FATAL(kprobes != NULL);
-	CU_TEST(strstr(kprobes, &KPROBE_1[2]) != NULL);
-	free(kprobes);
-
-	ret = tracefs_instance_file_append(NULL, KPROB_EVTS, KPROBE_2);
-	CU_TEST(ret == strlen(KPROBE_2));
-	kprobes = tracefs_instance_file_read(NULL, KPROB_EVTS, &size);
-	CU_TEST_FATAL(kprobes != NULL);
-	CU_TEST(strstr(kprobes, &KPROBE_2[2]) != NULL);
-	free(kprobes);
-
-	ret = tracefs_instance_file_append(NULL, KPROB_EVTS, KPROBE_1_RM);
-	CU_TEST(ret == strlen(KPROBE_1_RM));
-	kprobes = tracefs_instance_file_read(NULL, KPROB_EVTS, &size);
-	CU_TEST_FATAL(kprobes != NULL);
-	CU_TEST(strstr(kprobes, &KPROBE_1[2]) == NULL);
-	free(kprobes);
-
-	ret = tracefs_instance_file_clear(NULL, KPROB_EVTS);
+	ret = tracefs_kprobe_clear_all(true);
+	CU_TEST(ret == 0);
+	ret = tracefs_kprobe_raw(NULL, KPROBE_1_NAME, KPROBE_1_ADDR, KPROBE_1_FMT);
+	CU_TEST(ret == 0);
+	ret = tracefs_kprobe_raw(KPROBE_2_GROUP, KPROBE_2_NAME, KPROBE_2_ADDR,
+				 KPROBE_2_FMT);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_kretprobe_raw(KPROBE_2_GROUP, KRETPROBE_NAME, KRETPROBE_ADDR,
+				 KRETPROBE_FMT);
 	CU_TEST(ret == 0);
-	kprobes = tracefs_instance_file_read(NULL, KPROB_EVTS, &size);
-	CU_TEST(kprobes == NULL);
+
+	type = tracefs_kprobe_info(KPROBE_1_GROUP, KPROBE_1_NAME, &ktype,
+				   &kaddr, &kformat);
+	CU_TEST(type == TRACEFS_KPROBE);
+	CU_TEST(ktype && *ktype == 'p');
+	CU_TEST(kaddr && !strcmp(kaddr, KPROBE_1_ADDR));
+	CU_TEST(kformat && !strcmp(kformat, KPROBE_1_FMT));
+	free(ktype);
+	free(kaddr);
+	free(kformat);
+
+	type = tracefs_kprobe_info(KPROBE_2_GROUP, KPROBE_2_NAME, &ktype,
+				   &kaddr, &kformat);
+	CU_TEST(type == TRACEFS_KPROBE);
+	CU_TEST(ktype && *ktype == 'p');
+	CU_TEST(kaddr && !strcmp(kaddr, KPROBE_2_ADDR));
+	CU_TEST(kformat && !strcmp(kformat, KPROBE_2_FMT));
+	free(ktype);
+	free(kaddr);
+	free(kformat);
+
+	type = tracefs_kprobe_info(KPROBE_2_GROUP, KRETPROBE_NAME, &ktype,
+				   &kaddr, &kformat);
+	CU_TEST(type == TRACEFS_KRETPROBE);
+	CU_TEST(ktype && *ktype == 'r');
+	CU_TEST(kaddr && !strcmp(kaddr, KRETPROBE_ADDR));
+	CU_TEST(kformat && !strcmp(kformat, KRETPROBE_FMT));
+	free(ktype);
+	free(kaddr);
+	free(kformat);
+
+	kprobes = tracefs_get_kprobes(TRACEFS_ALL_KPROBES);
+	CU_TEST(kprobes != NULL);
+
+	for (i = 0; kprobes[i]; i++) {
+		char *system = strtok(kprobes[i], "/");
+		char *event = strtok(NULL, "");
+		bool found = false;
+		if (!strcmp(system, KPROBE_1_GROUP)) {
+			CU_TEST(!strcmp(event, KPROBE_1_NAME));
+			found = true;
+		} else if (!strcmp(system, KPROBE_2_GROUP)) {
+			switch (tracefs_kprobe_info(system, event, NULL, NULL, NULL)) {
+			case TRACEFS_KPROBE:
+				CU_TEST(!strcmp(event, KPROBE_2_NAME));
+				found = true;
+				break;
+			case TRACEFS_KRETPROBE:
+				CU_TEST(!strcmp(event, KRETPROBE_NAME));
+				found = true;
+				break;
+			default:
+				break;
+			}
+		}
+		CU_TEST(found);
+	}
+	tracefs_list_free(kprobes);
+	CU_TEST(i == 3);
+
+	kprobes = tracefs_get_kprobes(TRACEFS_KPROBE);
+	CU_TEST(kprobes != NULL);
+
+	for (i = 0; kprobes[i]; i++) {
+		char *system = strtok(kprobes[i], "/");
+		char *event = strtok(NULL, "");
+		bool found = false;
+		if (!strcmp(system, KPROBE_1_GROUP)) {
+			CU_TEST(!strcmp(event, KPROBE_1_NAME));
+			found = true;
+		} else if (!strcmp(system, KPROBE_2_GROUP)) {
+			CU_TEST(!strcmp(event, KPROBE_2_NAME));
+			found = true;
+		}
+		CU_TEST(found);
+	}
+	tracefs_list_free(kprobes);
+	CU_TEST(i == 2);
+
+	kprobes = tracefs_get_kprobes(TRACEFS_KRETPROBE);
+	CU_TEST(kprobes != NULL);
+
+	for (i = 0; kprobes[i]; i++) {
+		char *system = strtok(kprobes[i], "/");
+		char *event = strtok(NULL, "");
+		bool found = false;
+		if (!strcmp(system, KPROBE_2_GROUP)) {
+			CU_TEST(!strcmp(event, KRETPROBE_NAME));
+			found = true;
+		}
+		CU_TEST(found);
+	}
+	tracefs_list_free(kprobes);
+	CU_TEST(i == 1);
+
+	ret = tracefs_event_enable(instance, KPROBE_1_GROUP, KPROBE_1_NAME);
+	CU_TEST(ret == 0);
+	ret = tracefs_event_enable(instance, KPROBE_2_GROUP, KPROBE_2_NAME);
+	CU_TEST(ret == 0);
+	ret = tracefs_event_enable(instance, KPROBE_2_GROUP, KRETPROBE_NAME);
+	CU_TEST(ret == 0);
+
+	ret = tracefs_kprobe_clear_all(false);
+	CU_TEST(ret < 0);
+
+	ret = tracefs_kprobe_clear_probe(KPROBE_2_GROUP, NULL, false);
+	CU_TEST(ret < 0);
+
+	ret = tracefs_kprobe_clear_probe(KPROBE_2_GROUP, NULL, true);
+	CU_TEST(ret == 0);
+
+	kprobes = tracefs_get_kprobes(TRACEFS_ALL_KPROBES);
+	CU_TEST(kprobes != NULL);
+
+	for (i = 0; kprobes[i]; i++) {
+		char *system = strtok(kprobes[i], "/");
+		char *event = strtok(NULL, "");
+		bool found = false;
+		if (!strcmp(system, KPROBE_1_GROUP)) {
+			CU_TEST(!strcmp(event, KPROBE_1_NAME));
+			found = true;
+		}
+		CU_TEST(found);
+	}
+	tracefs_list_free(kprobes);
+	CU_TEST(i == 1);
+
+	ret = tracefs_kprobe_clear_all(true);
+	CU_TEST(ret == 0);
+
+	kprobes = tracefs_get_kprobes(TRACEFS_ALL_KPROBES);
+	CU_TEST(kprobes != NULL);
+
+	CU_TEST(kprobes[0] == NULL);
+	tracefs_list_free(kprobes);
 
 	tracefs_put_tracing_file(inst_file);
 	free(fname);
-- 
2.30.2


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

end of thread, other threads:[~2021-07-02 20:17 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-02 20:17 [PATCH v7 00/10] libtracefs: Facilitate adding and removing kprobes Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 01/10] libtracefs: Implement tracefs_instances() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 02/10] libtracefs: Implement tracefs_kprobe_raw() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 03/10] libtracefs: Implement tracefs_kretprobe_raw() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 04/10] libtracefs: Implement tracefs_get_kprobes() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 05/10] libtracefs: Add helper function to parse kprobes Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 06/10] libtracefs: Implement tracefs_kprobe_info() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 07/10] libtracefs: Implement tracefs_kprobe_clear_all() to remove all kprobes Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 08/10] libtracefs: Implement tracefs_kprobe_clear_probe() Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 09/10] libtracefs: Add man pages for kprobe functions Steven Rostedt
2021-07-02 20:17 ` [PATCH v7 10/10] libtracefs: Update the unit tests to use the kprobe API instead Steven Rostedt

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.