[DTrace-devel] [PATCH 29/47] Implement BEGIN and END probes for the dtrace provider

Kris Van Hees kris.van.hees at oracle.com
Sun May 3 20:17:47 PDT 2020


Add BEGIN and END probes as uprobes on libdtrace functions BEGIN_probe()
and END_probe(), which were added as part of an earlier patch.

By default, uprobes belong to group "uprobes", but we introduce a new
group, "dtrace_$pid", with probes that are specific to our consumer
process.  Specifically, this means that multiple consumers, with
distinct or changing libdtrace, can coexist.  We try to clean up our
BEGIN and END probes at close, and the sdt provider must recognize that
dtrace_$pid is a group to ignore.

Orabug: 31220513
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
Reviewed-by: Eugene Loh <eugene.loh at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/dt_prov_dtrace.c | 143 +++++++++++++++++++++++++++++++++++++
 libdtrace/dt_prov_sdt.c    |   7 ++
 libdtrace/dt_provider.h    |   2 +
 libdtrace/libdtrace.ver    |   2 -
 4 files changed, 152 insertions(+), 2 deletions(-)

diff --git a/libdtrace/dt_prov_dtrace.c b/libdtrace/dt_prov_dtrace.c
index f17da3b6..b1513108 100644
--- a/libdtrace/dt_prov_dtrace.c
+++ b/libdtrace/dt_prov_dtrace.c
@@ -7,6 +7,7 @@
  * The core probe provider for DTrace for the BEGIN, END, and ERROR probes.
  */
 #include <assert.h>
+#include <errno.h>
 #include <string.h>
 
 #include <bpf_asm.h>
@@ -18,6 +19,10 @@ static const char		prvname[] = "dtrace";
 static const char		modname[] = "";
 static const char		funname[] = "";
 
+#define PROBE_FUNC_SUFFIX	"_probe"
+
+#define UPROBE_EVENTS		TRACEFS "uprobe_events"
+
 static const dtrace_pattr_t	pattr = {
 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
@@ -205,9 +210,147 @@ static void trampoline(dt_pcb_t *pcb, int haspred)
 	dt_irlist_append(dlp, dt_cg_node_alloc(lbl_exit, instr));
 }
 
+static char *uprobe_spec(dtrace_hdl_t *dtp, const char *prb)
+{
+	struct ps_prochandle	*P;
+	int			perr = 0;
+	char			*fun;
+	GElf_Sym		sym;
+	prsyminfo_t		si;
+	char			*spec = NULL;
+
+	fun = dt_alloc(dtp, strlen(prb) + strlen(PROBE_FUNC_SUFFIX) + 1);
+	if (fun == NULL)
+		return NULL;
+
+	strcpy(fun, prb);
+	strcat(fun, PROBE_FUNC_SUFFIX);
+
+	/* grab our process */
+	P = Pgrab(getpid(), 2, 0, NULL, &perr);
+	if (P == NULL) {
+		dt_free(dtp, fun);
+		return NULL;
+	}
+
+	/* look up function, get the map, and record */
+	if (Pxlookup_by_name(P, -1, PR_OBJ_EVERY, fun, &sym, &si) == 0) {
+		const prmap_t	*mapp;
+		size_t		len;
+
+		mapp = Paddr_to_map(P, sym.st_value);
+		if (mapp == NULL)
+			goto out;
+
+		if (mapp->pr_file->first_segment != mapp)
+			mapp = mapp->pr_file->first_segment;
+
+		len = snprintf(NULL, 0, "%s:0x%lx",
+			       mapp->pr_file->prf_mapname,
+			       sym.st_value - mapp->pr_vaddr) + 1;
+		spec = dt_alloc(dtp, len);
+		if (spec == NULL)
+			goto out;
+
+		snprintf(spec, len, "%s:0x%lx", mapp->pr_file->prf_mapname,
+			 sym.st_value - mapp->pr_vaddr);
+	}
+
+out:
+	dt_free(dtp, fun);
+	Prelease(P, PS_RELEASE_NORMAL);
+	Pfree(P);
+
+	return spec;
+}
+
+static int probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp,
+		      int *idp, int *argcp, dt_argdesc_t **argvp)
+{
+	char	*spec;
+	char	*fn = NULL;
+	int	fd;
+	FILE	*f;
+	int	rc = -ENOENT;
+	size_t	len;
+
+	*idp = -1;
+	*argcp = 0;			/* no arguments */
+	*argvp = NULL;
+
+	/* get a uprobe specification for this probe */
+	spec = uprobe_spec(dtp, prp->desc->prb);
+	if (spec == NULL)
+		return -ENOENT;
+
+	/* add a uprobe */
+	fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND);
+	if (fd == -1)
+		goto out;
+
+	rc = dprintf(fd, "p:" DTRACE_PAT "/%s %s\n",
+		     getpid(), prp->desc->prb, spec);
+	close(fd);
+	if (rc == -1)
+		goto out;
+
+	len = snprintf(NULL, 0, "%s" DTRACE_PAT "/%s/format",
+		       EVENTSFS, getpid(), prp->desc->prb) + 1;
+	fn = dt_alloc(dtp, len);
+	if (fn == NULL)
+		goto out;
+
+	snprintf(fn, len, "%s" DTRACE_PAT "/%s/format",
+		 EVENTSFS, getpid(), prp->desc->prb);
+	f = fopen(fn, "r");
+	if (f == NULL)
+		goto out;
+
+	rc = tp_event_info(dtp, f, 0, idp, NULL, NULL);
+	fclose(f);
+
+out:
+	dt_free(dtp, spec);
+	dt_free(dtp, fn);
+
+	return rc;
+}
+
+/*
+ * Try to clean up system resources that may have been allocated for this
+ * probe.
+ *
+ * If there is an event FD, we close it.
+ *
+ * We also try to remove any uprobe that may have been created for the probe.
+ * This is harmless for probes that didn't get created.  If the removal fails
+ * for some reason we are out of luck - fortunately it is not harmful to the
+ * system as a whole.
+ */
+static int probe_fini(dtrace_hdl_t *dtp, dt_probe_t *prp)
+{
+	int	fd;
+
+	if (prp->event_fd != -1) {
+		close(prp->event_fd);
+		prp->event_fd = -1;
+	}
+
+	fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND);
+	if (fd == -1)
+		return -1;
+
+	dprintf(fd, "-:" DTRACE_PAT "/%s\n", getpid(), prp->desc->prb);
+	close(fd);
+
+	return 0;
+}
+
 dt_provimpl_t	dt_dtrace = {
 	.name		= prvname,
 	.prog_type	= BPF_PROG_TYPE_KPROBE,
 	.populate	= &populate,
 	.trampoline	= &trampoline,
+	.probe_info	= &probe_info,
+	.probe_fini	= &probe_fini,
 };
diff --git a/libdtrace/dt_prov_sdt.c b/libdtrace/dt_prov_sdt.c
index 64edc839..ff3560a7 100644
--- a/libdtrace/dt_prov_sdt.c
+++ b/libdtrace/dt_prov_sdt.c
@@ -38,6 +38,7 @@ static const char		modname[] = "vmlinux";
 
 #define KPROBES			"kprobes:"
 #define SYSCALLS		"syscalls:"
+#define UPROBES			"uprobes:"
 
 /*
  * All tracing events (tracepoints) include a number of fields that we need to
@@ -245,6 +246,8 @@ static int populate(dtrace_hdl_t *dtp)
 		return 0;
 
 	while (fgets(buf, sizeof(buf), f)) {
+		int	dummy;
+
 		p = strchr(buf, '\n');
 		if (p)
 			*p = '\0';
@@ -254,10 +257,14 @@ static int populate(dtrace_hdl_t *dtp)
 			if (dt_probe_insert(dtp, prv, prvname, modname, "",
 					    buf))
 				n++;
+		} else if (sscanf(buf, DTRACE_PAT ":", &dummy) == 1) {
+			continue;
 		} else if (memcmp(buf, KPROBES, sizeof(KPROBES) - 1) == 0) {
 			continue;
 		} else if (memcmp(buf, SYSCALLS, sizeof(SYSCALLS) - 1) == 0) {
 			continue;
+		} else if (memcmp(buf, UPROBES, sizeof(UPROBES) - 1) == 0) {
+			continue;
 		} else {
 			*p++ = '\0';
 
diff --git a/libdtrace/dt_provider.h b/libdtrace/dt_provider.h
index 0e4368ab..07ac174a 100644
--- a/libdtrace/dt_provider.h
+++ b/libdtrace/dt_provider.h
@@ -19,6 +19,8 @@ extern "C" {
 #define TRACEFS		"/sys/kernel/debug/tracing/"
 #define EVENTSFS	TRACEFS "events/"
 
+#define DTRACE_PAT	"dtrace_%d"
+
 struct dt_probe;
 
 /*
diff --git a/libdtrace/libdtrace.ver b/libdtrace/libdtrace.ver
index 6d617b6c..6c7ea341 100644
--- a/libdtrace/libdtrace.ver
+++ b/libdtrace/libdtrace.ver
@@ -101,6 +101,4 @@ LIBDTRACE_1.0 {
 LIBDTRACE_PRIVATE
 {
 	dtrace__internal_*;
-	BEGIN_probe;
-	END_probe;
 };
-- 
2.26.0




More information about the DTrace-devel mailing list