diff mbox series

[RFC,bpf-next,2/4] libbpf: support usdt provider/probe name-based attach for uprobes

Message ID 1642004329-23514-3-git-send-email-alan.maguire@oracle.com (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series libbpf: userspace attach by name | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for bpf-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 10 of 10 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 34 this patch: 34
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next fail VM_Test
bpf/vmtest-bpf-next-PR fail PR summary

Commit Message

Alan Maguire Jan. 12, 2022, 4:18 p.m. UTC
Add support for name-based attach to Userland Static-Defined Tracing
(USDT) probes via lookup of ELF notes associated with probe definition.
ELF notes are consulted for probe offset, and - if "is-enabled" style
of probing is in use - semaphore offset.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/lib/bpf/libbpf.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++----
 tools/lib/bpf/libbpf.h |  9 +++++-
 2 files changed, 86 insertions(+), 7 deletions(-)
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index bccc26a..cdcd799 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10275,6 +10275,52 @@  static ssize_t find_elf_func_offset(Elf *elf, const char *name)
 	return ret;
 }
 
+struct sdt_note {
+	uint64_t	pc;
+	uint64_t	base_addr;
+	uint64_t	semaphore;
+	const char	provider_probe[256];
+};
+
+/* Find offset of USDT probe in object specified in ELF notes.
+ * Note may also specify semaphore offset, record value in *semaphore_offp.
+ */
+static ssize_t find_elf_usdt_offset(Elf *elf, const char *provider,
+				    const char *name, ssize_t *semaphore_offp)
+{
+	Elf_Data *data = NULL;
+	Elf_Scn *scn = NULL;
+
+	while ((scn = find_elfscn(elf, SHT_NOTE, scn)) > 0) {
+		while ((data = elf_getdata(scn, data)) != 0) {
+			size_t name_off, desc_off, off;
+			GElf_Nhdr nhdr;
+
+			while ((off = gelf_getnote(data, off, &nhdr,
+						   &name_off, &desc_off)) != 0) {
+				struct sdt_note *sdt_note;
+				const char *probe;
+
+				if (nhdr.n_namesz != 8 ||
+				    memcmp((char *)data->d_buf + name_off, "stapsdt", 8) != 0)
+					continue;
+				sdt_note = (struct sdt_note *)(data->d_buf + desc_off);
+				if (strcmp(provider, sdt_note->provider_probe) != 0)
+					continue;
+				/* probe is after null-terminated provider */
+				probe = sdt_note->provider_probe +
+					strlen(sdt_note->provider_probe) + 1;
+				if (strcmp(probe, name) != 0)
+					continue;
+
+				*semaphore_offp = (ssize_t)sdt_note->semaphore;
+				return (ssize_t)sdt_note->pc;
+			}
+		}
+	}
+	return -ENOENT;
+}
+
 LIBBPF_API struct bpf_link *
 bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
 				const char *binary_path, size_t func_offset,
@@ -10286,7 +10332,7 @@  static ssize_t find_elf_func_offset(Elf *elf, const char *name)
 	size_t ref_ctr_off;
 	int pfd, err;
 	bool retprobe, legacy;
-	const char *func_name;
+	const char *func_name, *usdt_name, *usdt_provider;
 
 	if (!OPTS_VALID(opts, bpf_uprobe_opts))
 		return libbpf_err_ptr(-EINVAL);
@@ -10296,11 +10342,25 @@  static ssize_t find_elf_func_offset(Elf *elf, const char *name)
 	pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
 
 	func_name = OPTS_GET(opts, func_name, NULL);
-	if (func_name) {
-		ssize_t sym_off, rel_off;
+	usdt_provider = OPTS_GET(opts, usdt_provider, NULL);
+	usdt_name = OPTS_GET(opts, usdt_name, NULL);
+	if (func_name || usdt_name) {
+		ssize_t sym_off, rel_off, semaphore_off = 0;
 		Elf *elf;
 		int fd;
 
+		if (func_name && usdt_name) {
+			pr_warn("both func_name and usdt_name cannot be specified\n");
+			return libbpf_err_ptr(-EINVAL);
+		}
+		if (usdt_name && (func_offset || ref_ctr_off)) {
+			pr_warn("func_offset argument, ref_ctr_off option should be 0 when usdt_name is used\n");
+			return libbpf_err_ptr(-EINVAL);
+		}
+		if (usdt_name && !usdt_provider) {
+			pr_warn("usdt_provider and usdt_name must be supplied\n");
+			return libbpf_err_ptr(-EINVAL);
+		}
 		if (pid == -1) {
 			/* system-wide probing is not supported; we need
 			 * a running process to determine offsets.
@@ -10330,20 +10390,32 @@  static ssize_t find_elf_func_offset(Elf *elf, const char *name)
 			close(fd);
 			return libbpf_err_ptr(-LIBBPF_ERRNO__FORMAT);
 		}
-		sym_off = find_elf_func_offset(elf, func_name);
+		if (func_name)
+			sym_off = find_elf_func_offset(elf, func_name);
+		else
+			sym_off = find_elf_usdt_offset(elf, usdt_provider, usdt_name,
+						       &semaphore_off);
 		close(fd);
 		elf_end(elf);
 		if (sym_off < 0) {
-			pr_debug("could not find sym offset for %s\n", func_name);
+			pr_debug("could not find sym offset for %s\n", func_name ?: usdt_name);
 			return libbpf_err_ptr(sym_off);
 		}
 		rel_off = get_rel_offset(pid, sym_off);
 		if (rel_off < 0) {
 			pr_debug("could not find relative offset for %s at 0x%lx\n",
-				 func_name, sym_off);
+				 func_name ?: usdt_name, sym_off);
 			return libbpf_err_ptr(rel_off);
 		}
 		func_offset += (size_t)rel_off;
+		if (semaphore_off) {
+			ref_ctr_off = get_rel_offset(pid, semaphore_off);
+			if (ref_ctr_off < 0) {
+				pr_debug("could not find relative offset for semaphore for %s at 0x%lx\n",
+					 usdt_name, semaphore_off);
+				return libbpf_err_ptr(ref_ctr_off);
+			}
+		}
 	}
 
 	legacy = determine_uprobe_perf_type() < 0;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 40cb5ae..fcad6b1 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -439,9 +439,16 @@  struct bpf_uprobe_opts {
 	 * argument to specify argument _within_ the function.
 	 */
 	const char *func_name;
+	/* name of USDT (Userland Static-Defined Tracing) provider/probe.
+	 * Offsets of USDT probe and associated semaphore (if any) are found
+	 * in ELF notes.  Note that if usdt_name is specified, func_offset
+	 * argument and ref_ctr_offset values should be zero.
+	 */
+	const char *usdt_provider;
+	const char *usdt_name;
 	size_t :0;
 };
-#define bpf_uprobe_opts__last_field func_name
+#define bpf_uprobe_opts__last_field usdt_name
 
 /**
  * @brief **bpf_program__attach_uprobe()** attaches a BPF program