[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