[DTrace-devel] [PATCH v4 08/23] dtprobed: fill out the DOF stash

Nick Alcock nick.alcock at oracle.com
Wed Feb 21 20:48:02 UTC 2024


This commit arranges to create DOF stash entries corresponding to the probes
received by dtprobed when a DTRACEHIOC_ADDDOF is received from one of its
DOF-containing CUSE clients, and to remove them again when a
DTRACHIOC_REMOVE is received: at startup and at intervals (every 128
requests, an arbitrary value), the stash is scanned to remove entries
relating to processes that died before they had a chance to send a
DTRACEHIOC_REMOVE.

The stash is located under /run/dtrace by default, or under the directory
given by the new -s option to dtprobed.  This is mostly going to be used by
in-tree testing.  DTrace will gain a new dofstashpath option for the same
reason.  There is no need to document these: like dtprobed's -n option and
the DTRACE_DOF_INIT_DEVNAME environment variable, these are really only
testsuite internal implementation details.

Nothing is yet done with the stash: dtprobed still creates probes itself.

DOF-containing processes doing fork and exec have no effect on the stash as
yet: this will be fixed in a later commit.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
 dtprobed/dtprobed.c       | 92 +++++++++++++++++++++++++++++++++------
 dtprobed/dtprobed.service |  2 +
 dtrace.spec               |  4 ++
 3 files changed, 84 insertions(+), 14 deletions(-)

diff --git a/dtprobed/dtprobed.c b/dtprobed/dtprobed.c
index 938a234340c97..76c895ec58b76 100644
--- a/dtprobed/dtprobed.c
+++ b/dtprobed/dtprobed.c
@@ -55,6 +55,7 @@
 #include <dt_list.h>
 #include "dof_parser.h"
 #include "uprobes.h"
+#include "dof_stash.h"
 #include "libproc.h"
 
 #include "seccomp-assistance.h"
@@ -82,7 +83,8 @@ static void helper_ioctl(fuse_req_t req, int cmd, void *arg,
 			 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
 
 static int process_dof(fuse_req_t req, int out, int in, pid_t pid,
-		       dof_helper_t *dh, const void *in_buf);
+		       dof_helper_t *dh, const void *in_buf, size_t in_bufsz,
+		       uintptr_t buf_addr);
 
 static const struct cuse_lowlevel_ops dtprobed_clop = {
 	.ioctl = helper_ioctl,
@@ -458,6 +460,7 @@ helper_ioctl(fuse_req_t req, int cmd, void *arg,
 	dtprobed_userdata_t *userdata = get_userdata(pid);
 	const char *errmsg;
 	const void *buf;
+	int gen = 0;
 
 	/*
 	 * We can just ignore FUSE_IOCTL_COMPAT: the 32-bit and 64-bit versions
@@ -467,8 +470,11 @@ helper_ioctl(fuse_req_t req, int cmd, void *arg,
 	switch (cmd) {
 	case DTRACEHIOC_ADDDOF:
 		break;
-	case DTRACEHIOC_REMOVE: /* TODO */
-		fuse_reply_ioctl(req, 0, NULL, 0);
+	case DTRACEHIOC_REMOVE:
+		fuse_log(FUSE_LOG_DEBUG, "DTRACEHIOC_REMOVE from PID %i, generation %lu\n",
+			 pid, (uintptr_t) arg);
+		gen = dof_stash_remove(pid, (uintptr_t) arg);
+		fuse_reply_ioctl(req, gen, NULL, 0);
 		return;
 	default: errmsg = "invalid ioctl";;
 		fuse_log(FUSE_LOG_WARNING, "%i: dtprobed: %s %x\n",
@@ -642,10 +648,11 @@ helper_ioctl(fuse_req_t req, int cmd, void *arg,
 		buf = userdata->buf;
 
 	if (process_dof(req, parser_out_pipe, parser_in_pipe, pid,
-			&userdata->dh, buf) < 0)
+			&userdata->dh, buf, userdata->dof_hdr.dofh_loadsz,
+			userdata->dh.dofhp_dof) < 0)
 		goto process_err;
 
-	if (fuse_reply_ioctl(req, 0, NULL, 0) < 0)
+	if (fuse_reply_ioctl(req, gen, NULL, 0) < 0)
 		goto process_err;
 
 	free(userdata->buf);
@@ -658,6 +665,7 @@ helper_ioctl(fuse_req_t req, int cmd, void *arg,
 	 */
 	if (rq_count++ > CLEANUP_INTERVAL) {
 		cleanup_userdata();
+		dof_stash_prune_dead();
 		rq_count = 0;
 	}
 
@@ -690,14 +698,18 @@ helper_ioctl(fuse_req_t req, int cmd, void *arg,
  */
 static int
 process_dof(fuse_req_t req, int out, int in, pid_t pid,
-	    dof_helper_t *dh, const void *in_buf)
+	    dof_helper_t *dh, const void *in_buf, size_t in_bufsz,
+	    uintptr_t buf_addr)
 {
 	int perr = 0;
 	ps_prochandle *P;
 	dof_parsed_t *provider;
-	const char *errmsg;
 	size_t i;
 	size_t tries = 0;
+	int gen = 0;
+	const char *errmsg;
+	dt_list_t accum = {0};
+	const prmap_t *mapp;
 
 	if ((P = Pgrab(pid, 2, 0, NULL, &perr)) == NULL) {
 		fuse_log(FUSE_LOG_ERR, "%i: dtprobed: process grab failed: %s\n",
@@ -705,7 +717,15 @@ process_dof(fuse_req_t req, int out, int in, pid_t pid,
 		goto proc_err;
 	}
 
-	do {
+	mapp = Paddr_to_map(P, buf_addr);
+
+	if (mapp == NULL) {
+		fuse_log(FUSE_LOG_ERR, "%i: dtprobed: cannot look up mapping (process dead?)\n",
+			 pid);
+		goto release_err;
+	}
+
+        do {
 		errmsg = "DOF parser write failed";
 		while ((errno = dof_parser_host_write(out, dh,
 						      (dof_hdr_t *) in_buf)) == EAGAIN);
@@ -731,6 +751,9 @@ process_dof(fuse_req_t req, int out, int in, pid_t pid,
 		break;
 	} while (!provider);
 
+	if (dof_stash_push_parsed(&accum, provider) < 0)
+		goto oom;
+
 	for (i = 0; i < provider->provider.nprobes; i++) {
 		dof_parsed_t *probe = dof_read(pid, in);
 		size_t j;
@@ -739,6 +762,9 @@ process_dof(fuse_req_t req, int out, int in, pid_t pid,
 		if (!probe || probe->type != DIT_PROBE)
 			goto err;
 
+		if (dof_stash_push_parsed(&accum, probe) < 0)
+			goto oom;
+
 		for (j = 0; j < probe->probe.ntp; j++) {
 			dof_parsed_t *tp = dof_read(pid, in);
 
@@ -751,27 +777,48 @@ process_dof(fuse_req_t req, int out, int in, pid_t pid,
 			 * as we can, even if creation of some of them fails.
 			 */
 			create_probe(P, provider, probe, tp);
-			free(tp);
+			if (dof_stash_push_parsed(&accum, tp) < 0)
+				goto oom;
 		}
-		free(probe);
 	}
-	free(provider);
 
 	Prelease(P, PS_RELEASE_NORMAL);
 	Pfree(P);
+	if ((gen = dof_stash_add(pid, mapp->pr_dev, mapp->pr_inum, dh, in_buf, in_bufsz)) < 0)
+		goto fileio;
 
-	return 0;
+	if (dof_stash_write_parsed(pid, mapp->pr_dev, mapp->pr_inum, &accum) < 0) {
+		dof_stash_free(&accum);
+		dof_stash_remove(pid, gen);
+		goto fileio;
+	}
+	dof_stash_free(&accum);
+
+        return gen;
+
+oom:
+	fuse_log(FUSE_LOG_ERR, "%i: out of memory parsing DOF\n", pid);
+	goto proc_err;
 
 err:
 	fuse_log(FUSE_LOG_ERR, "%i: dtprobed: parser error: %s\n", pid, errmsg);
+	Prelease(P, PS_RELEASE_NORMAL);
+	Pfree(P);
+	goto proc_err;
 
+fileio:
+        fuse_log(FUSE_LOG_ERR, "%i: dtprobed: I/O error stashing DOF: %s\n",
+                 pid, strerror(errno));
+
+release_err:
 	Prelease(P, PS_RELEASE_NORMAL);
 	Pfree(P);
 
 proc_err:
+	dof_stash_free(&accum);
 	dof_parser_tidy(1);
 	return -1;
-}	
+}
 
 #if HAVE_LIBFUSE3
 static int
@@ -872,6 +919,7 @@ main(int argc, char *argv[])
 {
 	int opt;
 	char *devname = "dtrace/helper";
+	char *statedir = NULL;
 	int ret;
 	struct sigaction sa = {0};
 
@@ -882,7 +930,7 @@ main(int argc, char *argv[])
 	char *fuse_argv[] = { argv[0], "-f", "-s", NULL, NULL };
 	int fuse_argc = 3;
 
-	while ((opt = getopt(argc, argv, "Fdn:t:")) != -1) {
+	while ((opt = getopt(argc, argv, "Fs:dn:t:")) != -1) {
 		switch (opt) {
 		case 'F':
 			foreground = 1;
@@ -896,6 +944,13 @@ main(int argc, char *argv[])
 				fuse_argv[fuse_argc++] = "-d";
 			}
 			break;
+		case 's':
+			/*
+			 * This option is purely for the testsuite, so does not
+			 * appear in the usage() description.
+			 */
+			statedir = strdup(optarg);
+			break;
 		case 't':
 			timeout = atoi(optarg);
 			if (timeout <= 0) {
@@ -947,6 +1002,15 @@ main(int argc, char *argv[])
 
 	dof_parser_start();
 
+	if (dof_stash_init(statedir) < 0)
+		exit(1);
+
+	/*
+	 * Who knows what DOF-containing processes have died since the daemon
+	 * was running?  Clean them up.
+	 */
+	dof_stash_prune_dead();
+
 	if (!foreground) {
 		close(sync_fd);
 		sync_fd = -1;
diff --git a/dtprobed/dtprobed.service b/dtprobed/dtprobed.service
index 95f0a1d6d606a..8e491fe28ad13 100644
--- a/dtprobed/dtprobed.service
+++ b/dtprobed/dtprobed.service
@@ -20,6 +20,8 @@ ProtectHome=true
 PrivateDevices=false
 PrivateNetwork=true
 ProtectControlGroups=true
+RuntimeDirectory=dtrace
+RuntimeDirectoryPreserve=restart
 
 [Install]
 WantedBy=basic.target
diff --git a/dtrace.spec b/dtrace.spec
index 7af698d7c8271..278bdf53b7e1e 100644
--- a/dtrace.spec
+++ b/dtrace.spec
@@ -190,6 +190,10 @@ make DESTDIR=$RPM_BUILD_ROOT VERSION=%{version} \
      HDRPREFIX="$RPM_BUILD_ROOT/usr/include" \
      install install-test
 
+%if "%{?dist}" == ".el7"
+sed -i '/^ProtectSystem=/d; /^ProtectControlGroups=/d; /^RuntimeDirectory/d;' $RPM_BUILD_ROOT/usr/lib/systemd/system/dtprobed.service
+%endif
+
 # Because systemtap creates a sdt.h header file we have to rename
 # ours and then shift theirs out of the way.
 mv $RPM_BUILD_ROOT/usr/include/sys/sdt.h \
-- 
2.43.0.272.gce700b77fd




More information about the DTrace-devel mailing list