[DTrace-devel] [PATCH 4/4] selftests/usdt: add test covering different forms of USDT note args

Alan Maguire alan.maguire at oracle.com
Wed Jan 29 14:44:01 UTC 2025


Add a test exercising various arg types supported by USDT notes;
register values, register + offset and constants.  The test generates
a binary with probes represented as follows on x86_64:

Displaying notes found in: .note.stapsdt
  Owner                 Data size       Description
  stapsdt              0x00000048       NT_STAPSDT (SystemTap probe descriptors)
    Provider: test_prov
    Name: args
    Location: 0x0000000000400557, Base: 0x00000000004005f8, Semaphore: 0x0000000000000000
    Arguments: -4 at -4(%rbp) 8@%rax 8@%rdx -4@$18

Verify we get expected data for the probe arguments.

Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
---
 libdtrace/dt_pid.c                        | 61 ++++++++++++++---------
 test/unittest/usdt/tst.usdt-notes-args.r  |  2 +
 test/unittest/usdt/tst.usdt-notes-args.sh | 51 +++++++++++++++++++
 3 files changed, 91 insertions(+), 23 deletions(-)
 create mode 100644 test/unittest/usdt/tst.usdt-notes-args.r
 create mode 100755 test/unittest/usdt/tst.usdt-notes-args.sh

diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c
index 5608e380..cac52616 100644
--- a/libdtrace/dt_pid.c
+++ b/libdtrace/dt_pid.c
@@ -960,7 +960,7 @@ static int dt_usdt_note_parse_arg(char **argstr, struct dt_usdt_arg *a)
 
 static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 			       dtrace_probedesc_t *pdp, dt_pcb_t *pcb,
-			       const dt_provider_t *pvp, const char *path,
+			       const dt_provider_t *pvp, char *path,
 			       unsigned long base_addr)
 {
 	Elf *elf;
@@ -969,8 +969,10 @@ static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 	GElf_Nhdr nhdr;
 	size_t shstrndx, noff, doff, off, n;
 	Elf_Data *data;
+	GElf_Ehdr ehdr;
 	int i, ret = 0;
 	int fd = -1;
+	char *mod;
 
 	dt_dprintf("Scanning for USDT probes in ELF notes in '%s' (pid %i) matching %s:%s:%s\n",
 		   path, dpr->dpr_pid, pdp->mod, pdp->fun, pdp->prb);
@@ -982,17 +984,38 @@ static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 			     path, strerror(errno));
 		return -1;
 	}
+	mod = strrchr(path, '/');
+	if (mod)
+		mod++;
+	else
+		mod = path;
 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);   // ELF_C_READ ?
 	assert(elf_kind(elf) == ELF_K_ELF);
 	elf_getshdrstrndx(elf, &shstrndx);
 
+	if (gelf_getehdr(elf, &ehdr)) {
+		switch (ehdr.e_type) {
+		case ET_EXEC:
+			/* binary does not require base addr adjustment */
+			base_addr = 0;
+			break;
+		case ET_DYN:
+			break;
+		default:
+			dt_dprintf("unexpected ELF hdr type 0x%x for '%s'\n",
+				   ehdr.e_type, path);
+			ret = -1;
+			goto out;
+		}
+	}
+
 	while (1) {
 		char *secname;
 
 		scn = elf_nextscn(elf, scn);
 		if (scn == NULL) {
 			/* no ELF notes found, not an error */
-			return 0;
+			goto out;
 		}
 		assert(gelf_getshdr(scn, &shdr) != NULL);
 
@@ -1010,7 +1033,6 @@ static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 		pid_probespec_t psp = {0};
 		char *prv, *prb;
 		const char *fun;
-		char mod[PATH_MAX];
 		char *dbuf = (char *)data->d_buf;
 		long *addrs = data->d_buf + doff; /* 3 addrs are loc/base/semaphore */
 		GElf_Sym sym;
@@ -1057,12 +1079,7 @@ static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 			   prv, prb, path, addrs[0], addrs[1], nargs);
 		psp.pps_type = DTPPT_USDT;
 		psp.pps_prv = prv;
-		if (dt_Pobjname(dtp, dpr->dpr_pid, base_addr + addrs[0], mod,
-				sizeof(mod)) == NULL) {
-			dt_dprintf("cannot determine mod name for 0x%lx\n", addrs[0]);
-			mod[0] = '\0';
-		}
-		psp.pps_mod = basename(mod);
+		psp.pps_mod = mod;
 		psp.pps_prb = prb;
 		if (elf_getphdrnum(elf, &n))
 			continue;
@@ -1124,6 +1141,7 @@ static int dt_usdt_notes_parse(dtrace_hdl_t *dtp, dt_proc_t *dpr,
 			break;
 	}
 out:
+	elf_end(elf);
 	close(fd);
 	return ret;
 }
@@ -1557,9 +1575,10 @@ dt_pid_create_usdt_notes_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp,
 	const dt_provider_t *pvp;
 	char path[PATH_MAX + 1];
 	dt_proc_t *dpr = NULL;
+	char line[1024];
 	FILE *fp = NULL;
 	pid_t pid = 0;
-	int err;
+	int err = 0;
 
 	/* only specific pids are support for ELF notes for now... */
 	while (isdigit(*(pidstr - 1)))
@@ -1580,13 +1599,8 @@ dt_pid_create_usdt_notes_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp,
 	}
 	dpr = dt_proc_lookup(dtp, pid);
 	assert(dpr != NULL);
-	snprintf(path, sizeof(path), "/proc/%d/exe", dpr->dpr_pid);
-	err = dt_usdt_notes_parse(dtp, dpr, pdp, pcb, pvp, path, 0);
-	if (err)
-		goto out;
 
 	snprintf(path, sizeof(path), "/proc/%d/maps", pid);
-
 	fp = fopen(path, "r");
 	if (!fp) {
 		dt_pid_error(dtp, pcb, NULL, D_PROC_GRAB,
@@ -1594,20 +1608,21 @@ dt_pid_create_usdt_notes_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp,
 		err = 1;
 		goto out;
 	}
-	do {
+	while (fgets(line, sizeof(line) - 1, fp) != NULL) {
 		long addr_start, addr_end, file_offset;
 		long dev_major, dev_minor;
 		unsigned long inode;
 		char name[PATH_MAX + 1];
 		char perm[5];
+		int ret;
 
-
-		if (fscanf(fp,
-		       "%lx-%lx %4s %lx %lx:%lx %lu %[^\n]",
-		       &addr_start, &addr_end, perm, &file_offset,
-		       &dev_major, &dev_minor, &inode, name) != 8 ||
-		    !strchr(perm, 'x') || strchr(name, '[') != NULL)
+		ret = sscanf(line,
+			     "%lx-%lx %4s %lx %lx:%lx %lu %[^\n]",
+			     &addr_start, &addr_end, perm, &file_offset,
+			     &dev_major, &dev_minor, &inode, name);
+		if (ret != 8 || !strchr(perm, 'x') || strchr(name, '[') != NULL)
 			continue;
+
 		/* libstapsdt uses an memfd-based library to dynamically create
 		 * stapsdt notes for dynamic languages like python; we need
 		 * the associated /proc/<pid>/fds/ fd to read these notes.
@@ -1642,7 +1657,7 @@ dt_pid_create_usdt_notes_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp,
 				goto out;
 			}
 		}
-	} while (!feof(fp));
+	}
 out:
 	if (fp)
 		fclose(fp);
diff --git a/test/unittest/usdt/tst.usdt-notes-args.r b/test/unittest/usdt/tst.usdt-notes-args.r
new file mode 100644
index 00000000..42bca19f
--- /dev/null
+++ b/test/unittest/usdt/tst.usdt-notes-args.r
@@ -0,0 +1,2 @@
+test:main:args:2:./test:val:18
+
diff --git a/test/unittest/usdt/tst.usdt-notes-args.sh b/test/unittest/usdt/tst.usdt-notes-args.sh
new file mode 100755
index 00000000..7c8ea37b
--- /dev/null
+++ b/test/unittest/usdt/tst.usdt-notes-args.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+
+# This test covers all USDT probes fired by the DTRACE_PROBEn macros.
+# Arguments values are checked only for first 10 arguments because
+# there is support for arg0 ... arg9 only at this moment.
+
+if [ $# != 1 ]; then
+	echo expected one argument: '<'dtrace-path'>'
+	exit 2
+fi
+
+dtrace=$1
+CC=/usr/bin/gcc
+CFLAGS="-I${PWD}/test/unittest/usdt"
+
+DIRNAME="$tmpdir/usdt-notes.$$.$RANDOM"
+mkdir -p $DIRNAME
+cd $DIRNAME
+
+cat > test.c <<EOF
+#include <sdt_notes.h>
+
+int
+main(int argc, char **argv)
+{
+	DTRACE_PROBE4(test_prov, args, argc, argv[0], argv[1] + 4, 18);
+}
+EOF
+
+${CC} ${CFLAGS} -o test test.c
+if [ $? -ne 0 ]; then
+	echo "failed to compile test.c" >& 2
+	exit 1
+fi
+
+$dtrace -c './test arg1val' -qs /dev/stdin <<EOF
+test_prov\$target:::args
+{
+	printf("%s:%s:%s:%li:%s:%s:%li\n", probemod, probefunc, probename,
+	       arg0, copyinstr(arg1), copyinstr(arg2), arg3);
+}
+
+EOF
+status=$?
+
+exit $status
-- 
2.43.5




More information about the DTrace-devel mailing list