[DTrace-devel] [PATCH 3/3] add BEGIN and END probes

eugene.loh at oracle.com eugene.loh at oracle.com
Thu Mar 26 22:21:03 PDT 2020


From: Eugene Loh <eugene.loh at oracle.com>

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.

Note two naming conventions:
*)  We prefix many general functions with "dtrace".
*)  We prefix dt_provimpl_t functions with the provider name.
As a result, two specific functions would expect to have the name
dtrace_probe_info().  To resolve this conflict, we prefix dt_provimpl_t
functions with "dtrace_prov" for the dtrace provider.

The trampoline code was added in an earlier patch;  here we
just clean up some associated comments.

Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
 libdtrace/dt_prov_dtrace.c | 136 ++++++++++++++++++++++++++++++++++---
 libdtrace/dt_prov_fbt.c    |   2 +-
 libdtrace/dt_prov_sdt.c    |   7 +-
 3 files changed, 133 insertions(+), 12 deletions(-)

diff --git a/libdtrace/dt_prov_dtrace.c b/libdtrace/dt_prov_dtrace.c
index b2327dc2..da21279e 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>
@@ -22,7 +23,87 @@ static const dtrace_pattr_t	pattr = {
 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
 };
 
-static int dtrace_populate(dtrace_hdl_t *dtp)
+#define UPROBE_EVENTS		TRACEFS "uprobe_events"
+
+/*
+ * For a libdtrace function name, return path:offset.
+ */
+static char *dt_get_path_off(char *func) {
+	struct ps_prochandle *P;
+	int perr = 0;
+	GElf_Sym sym;
+	prsyminfo_t si;
+	const prmap_t *pmptr;
+	char *path_off = NULL;
+
+	/* grab our process */
+	P = Pgrab(getpid(), 2, 0, NULL, &perr);
+	if (P == NULL)
+		return NULL; /* FIXME: do something with perr? */
+
+	/* look up function, get the map, and record */
+	if (Pxlookup_by_name(P, -1, PR_OBJ_EVERY, func, &sym, &si) == 0
+	    && (pmptr = Paddr_to_map(P, sym.st_value)) != NULL
+	    && (path_off = malloc(strlen(pmptr->pr_file->prf_mapname) + 3 + 21)))
+		sprintf(path_off, "%s:0x%lx",
+		    pmptr->pr_file->prf_mapname, sym.st_value - pmptr->pr_vaddr);
+
+	Prelease(P, PS_RELEASE_NORMAL);
+	Pfree(P);
+	return path_off;
+}
+
+static int dtrace_prov_probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp,
+			     int *idp, int *argcp, dt_argdesc_t **argvp)
+{
+	FILE	*f;
+	int	rc = -1, fd;
+	char	*func;		/* libdtrace function we will probe */
+	char	*path_off;	/* path:offset for that function */
+	char	fmt[256];	/* format file */
+
+	*idp = -1;
+	*argcp = 0;
+	*argvp = NULL;
+
+	/* form function name for this probe */
+	if (strcmp(prp->desc->prb, "BEGIN") == 0) {
+		func = "BEGIN_probe";
+	} else if (strcmp(prp->desc->prb, "END") == 0) {
+		func = "END_probe";
+	} else if (strcmp(prp->desc->prb, "ERROR") == 0)
+		assert(0); /* FIXME: not yet supported */
+	else
+		assert(0); /* unknown probe */
+
+	/* form path:offset for the function */
+	path_off = dt_get_path_off(func);
+	if (path_off == NULL)
+		return -ENOENT;
+
+	/* add a uprobe for this function */
+	fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND);
+	if (fd != -1) {
+		rc = dprintf(fd, "p:dtrace_%d/%s %s\n", getpid(), prp->desc->prb, path_off);
+		close(fd);
+	}
+	free(path_off);
+	if (rc == -1)
+		return -ENOENT;
+
+	/* read the format file */
+	if (snprintf(fmt, sizeof (fmt), "%sdtrace_%d/%s/format", EVENTSFS, getpid(), prp->desc->prb) >= sizeof (fmt))
+		return -ENOENT;
+	f = fopen(fmt, "r");
+	if (f == NULL)
+		return -ENOENT;
+	rc = tp_event_info(dtp, f, 0, idp, NULL, NULL);
+	fclose(f);
+
+	return rc;
+}
+
+static int dtrace_prov_populate(dtrace_hdl_t *dtp)
 {
 	dt_provider_t	*prv;
 	int		n = 0;
@@ -42,7 +123,7 @@ static int dtrace_populate(dtrace_hdl_t *dtp)
 }
 
 /*
-* Generate a BPF trampoline for a dtrace probe (BEGIN, END, or ERROR).
+ * Generate a BPF trampoline for a dtrace probe (BEGIN, END, or ERROR).
  *
  * The trampoline function is called when a dtrace probe triggers, and it must
  * satisfy the following prototype:
@@ -53,7 +134,7 @@ static int dtrace_populate(dtrace_hdl_t *dtp)
  * function that implements tha compiled D clause.  It returns the value that
  * it gets back from that function.
  */
-static void dtrace_trampoline(dt_pcb_t *pcb, int haspred)
+static void dtrace_prov_trampoline(dt_pcb_t *pcb, int haspred)
 {
 	int		i;
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
@@ -183,7 +264,7 @@ static void dtrace_trampoline(dt_pcb_t *pcb, int haspred)
 	}
 
 	/*
-	 *     rc = dt_program(scd, dctx);
+	 *     rc = dt_program(regs, dctx);
 	 */
 	idp = dt_dlib_get_func(pcb->pcb_hdl, "dt_program");
 	assert(idp != NULL);
@@ -200,6 +281,39 @@ static void dtrace_trampoline(dt_pcb_t *pcb, int haspred)
 	dt_irlist_append(dlp, dt_cg_node_alloc(lbl_exit, instr));
 }
 
+static void dtrace_prov_cleanup(dtrace_hdl_t *dtp)
+{
+	char *probes[] = { "BEGIN", "END" };
+	dtrace_probedesc_t pdesc;
+	int i, fd;
+
+	/* start constructing a probe description */
+	pdesc.id = DTRACE_IDNONE;
+	pdesc.prv = "dtrace";
+	pdesc.mod = "";
+	pdesc.fun = "";
+
+	/* clear the BEGIN and END uprobes */
+	fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND);
+	if (fd == -1)
+		return;
+	for (i = 0; i < sizeof(probes) / sizeof(*probes); i++) {
+		dt_probe_t	*prb;
+
+		/* close the fd if possible */
+		pdesc.prb = probes[i];
+		prb = dt_probe_lookup(dtp, &pdesc);
+		if (prb && prb->event_fd != -1) {
+			close(prb->event_fd);
+			prb->event_fd = -1;
+		}
+
+		/* clear the UPROBE_EVENTS entry */
+		dprintf(fd, "-:dtrace_%d/%s\n", getpid(), probes[i]);
+	}
+	close(fd);
+}
+
 #if 0
 #define EVENT_PREFIX	"tracepoint/dtrace/"
 
@@ -208,7 +322,7 @@ static void dtrace_trampoline(dt_pcb_t *pcb, int haspred)
  * ELF section name).  We use an unused event group (dtrace) to be able to
  * fake a section name that libbpf will allow us to use.
  */
-static struct dt_probe *dtrace_resolve_event(const char *name)
+static struct dt_probe *dtrace_prov_resolve_event(const char *name)
 {
 	struct dt_probe	tmpl;
 	struct dt_probe	*probe;
@@ -232,7 +346,7 @@ static struct dt_probe *dtrace_resolve_event(const char *name)
 	return probe;
 }
 
-static int dtrace_attach(const char *name, int bpf_fd)
+static int dtrace_prov_attach(const char *name, int bpf_fd)
 {
 	return -1;
 }
@@ -241,10 +355,12 @@ static int dtrace_attach(const char *name, int bpf_fd)
 dt_provimpl_t	dt_dtrace = {
 	.name		= "dtrace",
 	.prog_type	= BPF_PROG_TYPE_KPROBE,
-	.populate	= &dtrace_populate,
-	.trampoline	= &dtrace_trampoline,
+	.populate	= &dtrace_prov_populate,
+	.trampoline	= &dtrace_prov_trampoline,
+	.probe_info	= &dtrace_prov_probe_info,
+	.cleanup	= &dtrace_prov_cleanup,
 #if 0
-	.resolve_event	= &dtrace_resolve_event,
-	.attach		= &dtrace_attach,
+	.resolve_event	= &dtrace_prov_resolve_event,
+	.attach		= &dtrace_prov_attach,
 #endif
 };
diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c
index cfaad8af..3262449c 100644
--- a/libdtrace/dt_prov_fbt.c
+++ b/libdtrace/dt_prov_fbt.c
@@ -356,7 +356,7 @@ static void fbt_trampoline(dt_pcb_t *pcb, int haspred)
 	}
 
 	/*
-	 *     rc = dt_program(scd, dctx);
+	 *     rc = dt_program(regs, dctx);
 	 */
 	idp = dt_dlib_get_func(pcb->pcb_hdl, "dt_program");
 	assert(idp != NULL);
diff --git a/libdtrace/dt_prov_sdt.c b/libdtrace/dt_prov_sdt.c
index 1dcaf584..1cbf28b9 100644
--- a/libdtrace/dt_prov_sdt.c
+++ b/libdtrace/dt_prov_sdt.c
@@ -42,6 +42,7 @@ static const char		modname[] = "vmlinux";
 
 #define KPROBES			"kprobes:"
 #define SYSCALLS		"syscalls:"
+#define DTRACE_PROV		"dtrace_"
 
 /*
  * All tracing events (tracepoints) include a number of fields that we need to
@@ -255,7 +256,9 @@ static int sdt_probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp,
  * The PROBE_LIST file lists all tracepoints in a <group>:<name> format.  When
  * kprobes are registered on the system, they will appear in this list also as
  * kprobes:<name>.  We need to ignore them because DTrace already accounts for
- * them as FBT probes.
+ * them as FBT probes.  Similarly for the syscalls and dtrace_* groups.  Indeed,
+ * dtrace_* groups might be leftover from previous runs that did not clean up
+ * properly.
  */
 static int sdt_populate(dtrace_hdl_t *dtp)
 {
@@ -286,6 +289,8 @@ static int sdt_populate(dtrace_hdl_t *dtp)
 			continue;
 		} else if (memcmp(buf, SYSCALLS, sizeof(SYSCALLS) - 1) == 0) {
 			continue;
+		} else if (memcmp(buf, DTRACE_PROV, sizeof(DTRACE_PROV) - 1) == 0) {
+			continue;
 		} else {
 			*p++ = '\0';
 
-- 
2.18.2




More information about the DTrace-devel mailing list