[DTrace-devel] [PATCH 8/8] fbt, rawfbt: consolidate code to avoid duplication
Kris Van Hees
kris.van.hees at oracle.com
Fri Mar 7 21:34:41 UTC 2025
After optimizing both fbt and rawfbt providers, the resulting code has
a significant amount of duplication. The rawfbt provider can now be
defined in terms of the kprobe-based fbt provider functions.
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
libdtrace/Build | 1 -
libdtrace/dt_prov_fbt.c | 131 +++++++++----
libdtrace/dt_prov_rawfbt.c | 393 -------------------------------------
3 files changed, 96 insertions(+), 429 deletions(-)
delete mode 100644 libdtrace/dt_prov_rawfbt.c
diff --git a/libdtrace/Build b/libdtrace/Build
index 51e0f078..7e6e8a38 100644
--- a/libdtrace/Build
+++ b/libdtrace/Build
@@ -55,7 +55,6 @@ libdtrace-build_SOURCES = dt_aggregate.c \
dt_prov_lockstat.c \
dt_prov_proc.c \
dt_prov_profile.c \
- dt_prov_rawfbt.c \
dt_prov_rawtp.c \
dt_prov_sched.c \
dt_prov_sdt.c \
diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c
index d837e14d..93ed270e 100644
--- a/libdtrace/dt_prov_fbt.c
+++ b/libdtrace/dt_prov_fbt.c
@@ -6,17 +6,26 @@
*
* The Function Boundary Tracing (FBT) provider for DTrace.
*
- * FBT probes are exposed by the kernel as kprobes. They are listed in the
- * TRACEFS/available_filter_functions file. Some kprobes are associated with
- * a specific kernel module, while most are in the core kernel.
+ * Kernnel functions can be traced through fentry/fexit probes (when available)
+ * and kprobes. The FBT provider supports both implementations and will use
+ * fentry/fexit probes if the kernel supports them, and fallback to kprobes
+ * otherwise. The FBT provider does not support tracing synthetic functions
+ * (i.e. compiler-generated functions with a . in their name).
+ *
+ * The rawfbt provider implements a variant of the FBT provider and always uses
+ * kprobes. This provider allow tracing of synthetic function.
*
* Mapping from event name to DTrace probe name:
*
* <name> fbt:vmlinux:<name>:entry
* fbt:vmlinux:<name>:return
+ * rawfbt:vmlinux:<name>:entry
+ * rawfbt:vmlinux:<name>:return
* or
* <name> [<modname>] fbt:<modname>:<name>:entry
* fbt:<modname>:<name>:return
+ * rawfbt:<modname>:<name>:entry
+ * rawfbt:<modname>:<name>:return
*/
#include <assert.h>
#include <errno.h>
@@ -57,18 +66,19 @@ static const dtrace_pattr_t pattr = {
dt_provimpl_t dt_fbt_fprobe;
dt_provimpl_t dt_fbt_kprobe;
+dt_provimpl_t dt_rawfbt;
/*
- * Create the fbt provider.
+ * Create the fbt and rawfbt providers.
*/
static int populate(dtrace_hdl_t *dtp)
{
- dt_provider_t *prv;
-
dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_fprobe : dt_fbt_kprobe;
- prv = dt_provider_create(dtp, prvname, &dt_fbt, &pattr, NULL);
- if (prv == NULL)
+ if (dt_provider_create(dtp, dt_fbt.name, &dt_fbt, &pattr,
+ NULL) == NULL ||
+ dt_provider_create(dtp, dt_rawfbt.name, &dt_rawfbt, &pattr,
+ NULL) == NULL)
return -1; /* errno already set */
return 0;
@@ -107,8 +117,6 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
dt_htab_next_t *it = NULL;
dtrace_probedesc_t pd;
- dt_modsym_mark_traceable(dtp);
-
/*
* Nothing to do if a probe name is specified and cannot match 'entry'
* or 'return'.
@@ -120,8 +128,11 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
if (prb == 0)
return 0;
- /* Synthetic function names are not supported for FBT. */
- if (strchr(pdp->fun, '.'))
+ /*
+ * Unless we are dealing with a rawfbt probe, synthetic functions are
+ * not supported.
+ */
+ if (strcmp(pdp->prv, dt_rawfbt.name) != 0 && strchr(pdp->fun, '.'))
return 0;
/*
@@ -134,6 +145,14 @@ static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
return 0;
}
+ /*
+ * Ensure that kernel symbols that are FBT-traceable are marked as
+ * such. We don't do this earlier in this function so that the
+ * preceding tests have the greatest opportunity to avoid doing this
+ * unnecessarily.
+ */
+ dt_modsym_mark_traceable(dtp);
+
/*
* If we have an explicit function name, we start with a basic symbol
* name lookup.
@@ -396,12 +415,12 @@ static int fprobe_prog_load(dtrace_hdl_t *dtp, const dt_probe_t *prp,
\*******************************/
/*
- * Generate a BPF trampoline for a FBT probe.
+ * Generate a BPF trampoline for a FBT (or rawfbt) probe.
*
* The trampoline function is called when a FBT probe triggers, and it must
* satisfy the following prototype:
*
- * int dt_fbt(dt_pt_regs *regs)
+ * int dt_(raw)fbt(dt_pt_regs *regs)
*
* The trampoline will populate a dt_dctx_t struct and then call the function
* that implements the compiled D clause. It returns 0 to the caller.
@@ -422,7 +441,7 @@ static int kprobe_trampoline(dt_pcb_t *pcb, uint_t exitlbl)
dt_cg_tramp_copy_rval_from_regs(pcb);
/*
- * fbt:::return arg0 should be the function offset for
+ * (raw)fbt:::return arg0 should be the function offset for
* return instruction. Since we use kretprobes, however,
* which do not fire until the function has returned to
* its caller, information about the returning instruction
@@ -441,11 +460,28 @@ static int kprobe_trampoline(dt_pcb_t *pcb, uint_t exitlbl)
static int kprobe_attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd)
{
+ const char *fun = prp->desc->fun;
+ char *tpn = (char *)fun;
+ int rc = -1;
+
if (!dt_tp_probe_has_info(prp)) {
char *fn;
FILE *f;
- size_t len;
- int fd, rc = -1;
+ int fd;
+
+ /*
+ * For rawfbt probes, we need to apply a . -> _ conversion to
+ * ensure the tracepoint name is valid.
+ */
+ if (strcmp(prp->desc->prv, dt_rawfbt.name) == 0) {
+ char *p;
+
+ tpn = strdup(fun);
+ for (p = tpn; *p; p++) {
+ if (*p == '.')
+ *p = '_';
+ }
+ }
/*
* Register the kprobe with the tracing subsystem. This will
@@ -453,41 +489,42 @@ static int kprobe_attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd)
*/
fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND);
if (fd == -1)
- return -ENOENT;
+ goto out;
rc = dprintf(fd, "%c:" FBT_GROUP_FMT "/%s %s\n",
prp->desc->prb[0] == 'e' ? 'p' : 'r',
- FBT_GROUP_DATA, prp->desc->fun, prp->desc->fun);
+ FBT_GROUP_DATA, tpn, fun);
close(fd);
if (rc == -1)
- return -ENOENT;
+ goto out;
/* create format file name */
- len = snprintf(NULL, 0, "%s" FBT_GROUP_FMT "/%s/format",
- EVENTSFS, FBT_GROUP_DATA, prp->desc->fun) + 1;
- fn = dt_alloc(dtp, len);
- if (fn == NULL)
- return -ENOENT;
-
- snprintf(fn, len, "%s" FBT_GROUP_FMT "/%s/format", EVENTSFS,
- FBT_GROUP_DATA, prp->desc->fun);
+ if (asprintf(&fn, "%s" FBT_GROUP_FMT "/%s/format", EVENTSFS,
+ FBT_GROUP_DATA, tpn) == -1)
+ goto out;
/* open format file */
f = fopen(fn, "r");
- dt_free(dtp, fn);
+ free(fn);
if (f == NULL)
- return -ENOENT;
+ goto out;
/* read event id from format file */
rc = dt_tp_probe_info(dtp, f, 0, prp, NULL, NULL);
fclose(f);
if (rc < 0)
- return -ENOENT;
+ goto out;
}
/* attach BPF program to the probe */
- return dt_tp_probe_attach(dtp, prp, bpf_fd);
+ rc = dt_tp_probe_attach(dtp, prp, bpf_fd);
+
+out:
+ if (tpn != prp->desc->fun)
+ free(tpn);
+
+ return rc == -1 ? -ENOENT : rc;
}
/*
@@ -503,7 +540,8 @@ static int kprobe_attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd)
*/
static void kprobe_detach(dtrace_hdl_t *dtp, const dt_probe_t *prp)
{
- int fd;
+ int fd;
+ char *tpn = (char *)prp->desc->fun;
if (!dt_tp_probe_has_info(prp))
return;
@@ -514,9 +552,20 @@ static void kprobe_detach(dtrace_hdl_t *dtp, const dt_probe_t *prp)
if (fd == -1)
return;
- dprintf(fd, "-:" FBT_GROUP_FMT "/%s\n", FBT_GROUP_DATA,
- prp->desc->fun);
+ if (strcmp(prp->desc->prv, dt_rawfbt.name) == 0) {
+ char *p;
+
+ for (p = tpn; *p; p++) {
+ if (*p == '.')
+ *p = '_';
+ }
+ }
+
+ dprintf(fd, "-:" FBT_GROUP_FMT "/%s\n", FBT_GROUP_DATA, tpn);
close(fd);
+
+ if (tpn != prp->desc->fun)
+ free(tpn);
}
dt_provimpl_t dt_fbt_fprobe = {
@@ -549,3 +598,15 @@ dt_provimpl_t dt_fbt = {
.name = prvname,
.populate = &populate,
};
+
+dt_provimpl_t dt_rawfbt = {
+ .name = "rawfbt",
+ .prog_type = BPF_PROG_TYPE_KPROBE,
+ .populate = &populate,
+ .provide = &provide,
+ .load_prog = &dt_bpf_prog_load,
+ .trampoline = &kprobe_trampoline,
+ .attach = &kprobe_attach,
+ .detach = &kprobe_detach,
+ .probe_destroy = &dt_tp_probe_destroy,
+};
diff --git a/libdtrace/dt_prov_rawfbt.c b/libdtrace/dt_prov_rawfbt.c
deleted file mode 100644
index 52152655..00000000
--- a/libdtrace/dt_prov_rawfbt.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * 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.
- *
- * The Raw Function Boundary Tracing provider for DTrace.
- *
- * The kernel provides kprobes to trace specific symbols. They are listed in
- * the TRACEFS/available_filter_functions file. Kprobes may be associated with
- * a symbol in the core kernel or with a symbol in a specific kernel module.
- * Whereas the fbt provider supports tracing regular symbols only, the rawfbt
- * provider also provides access to synthetic symbols, i.e. symbols created by
- * compiler optimizations.
- *
- * Mapping from event name to DTrace probe name:
- *
- * <name> rawfbt:vmlinux:<name>:entry
- * rawfbt:vmlinux:<name>:return
- * or
- * <name> [<modname>] rawfbt:<modname>:<name>:entry
- * rawfbt:<modname>:<name>:return
- */
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <linux/bpf.h>
-#include <linux/btf.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <bpf_asm.h>
-
-#include "dt_btf.h"
-#include "dt_dctx.h"
-#include "dt_cg.h"
-#include "dt_module.h"
-#include "dt_provider_tp.h"
-#include "dt_probe.h"
-#include "dt_pt_regs.h"
-
-static const char prvname[] = "rawfbt";
-
-#define KPROBE_EVENTS TRACEFS "kprobe_events"
-
-#define FBT_GROUP_FMT GROUP_FMT "_%s"
-#define FBT_GROUP_DATA GROUP_DATA, prp->desc->prb
-
-static const dtrace_pattr_t pattr = {
-{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
-{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
-{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
-};
-
-/*
- * Create the rawfbt provider.
- */
-static int populate(dtrace_hdl_t *dtp)
-{
- dt_provider_t *prv;
-
- prv = dt_provider_create(dtp, prvname, &dt_rawfbt, &pattr, NULL);
- if (prv == NULL)
- return -1; /* errno already set */
-
- return 0;
-}
-
-/* Create a probe (if it does not exist yet). */
-static int provide_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
-{
- dt_provider_t *prv = dt_provider_lookup(dtp, pdp->prv);
-
- if (prv == NULL)
- return 0;
- if (dt_probe_lookup(dtp, pdp) != NULL)
- return 0;
-#ifdef DEBUG_FBT
- if (dt_tp_probe_insert(dtp, prv, pdp->prv, pdp->mod, pdp->fun, pdp->prb)) {
- fprintf(stderr, "%s(..., PROVIDE %s:%s:%s:%s) - ...\n", __func__, pdp->prv, pdp->mod, pdp->fun, pdp->prb);
- return 1;
- }
-#else
- if (dt_tp_probe_insert(dtp, prv, pdp->prv, pdp->mod, pdp->fun, pdp->prb))
- return 1;
-#endif
-
- return 0;
-}
-
-/*
- * Try to provide probes for the given probe description. The caller ensures
- * that the provider name in probe desxcription (if any) is a match for this
- * provider. When this is called, we already know that this provider matches
- * the provider component of the probe specification.
- */
-#define FBT_ENTRY 1
-#define FBT_RETURN 2
-
-static int provide(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
-{
- int n = 0;
- int prb = 0;
- dt_module_t *dmp = NULL;
- dt_symbol_t *sym = NULL;
- dt_htab_next_t *it = NULL;
- dtrace_probedesc_t pd;
-
- dt_modsym_mark_traceable(dtp);
-
- /*
- * Nothing to do if a probe name is specified and cannot match 'entry'
- * or 'return'.
- */
- if (dt_gmatch("entry", pdp->prb))
- prb |= FBT_ENTRY;
- if (dt_gmatch("return", pdp->prb))
- prb |= FBT_RETURN;
- if (prb == 0)
- return 0;
-
- /*
- * If we have an explicit module name, check it. If not found, we can
- * ignore this request.
- */
- if (pdp->mod[0] != '\0' && strchr(pdp->mod, '*') == NULL) {
- dmp = dt_module_lookup_by_name(dtp, pdp->mod);
- if (dmp == NULL)
- return 0;
- }
-
- /*
- * If we have an explicit function name, we start with a basic symbol
- * name lookup.
- */
- if (pdp->fun[0] != '\0' && strchr(pdp->fun, '*') == NULL) {
- /* If we have a module, use it. */
- if (dmp != NULL) {
- sym = dt_module_symbol_by_name(dtp, dmp, pdp->fun);
- if (sym == NULL)
- return 0;
- if (!dt_symbol_traceable(sym))
- return 0;
-
- pd.id = DTRACE_IDNONE;
- pd.prv = pdp->prv;
- pd.mod = dmp->dm_name;
- pd.fun = pdp->fun;
-
- if (prb & FBT_ENTRY) {
- pd.prb = "entry";
- n += provide_probe(dtp, &pd);
- }
- if (prb & FBT_RETURN) {
- pd.prb = "return";
- n += provide_probe(dtp, &pd);
- }
-
- return n;
- }
-
- sym = dt_symbol_by_name(dtp, pdp->fun);
- while (sym != NULL) {
- const char *mod = dt_symbol_module(sym)->dm_name;
-
- if (dt_symbol_traceable(sym) &&
- dt_gmatch(mod, pdp->mod)) {
- pd.id = DTRACE_IDNONE;
- pd.prv = pdp->prv;
- pd.mod = mod;
- pd.fun = pdp->fun;
-
- if (prb & FBT_ENTRY) {
- pd.prb = "entry";
- n += provide_probe(dtp, &pd);
- }
- if (prb & FBT_RETURN) {
- pd.prb = "return";
- n += provide_probe(dtp, &pd);
- }
-
- }
- sym = dt_symbol_by_name_next(sym);
- }
-
- return n;
- }
-
- /*
- * No explicit function name. We need to go through all possible
- * symbol names and see if they match.
- */
- while ((sym = dt_htab_next(dtp->dt_kernsyms, &it)) != NULL) {
- dt_module_t *smp;
- const char *fun;
-
- /* Ensure the symbol can be traced. */
- if (!dt_symbol_traceable(sym))
- continue;
-
- /* Match the function name. */
- fun = dt_symbol_name(sym);
- if (!dt_gmatch(fun, pdp->fun))
- continue;
-
- /* Validate the module name. */
- smp = dt_symbol_module(sym);
- if (dmp) {
- if (smp != dmp)
- continue;
- } else if (!dt_gmatch(smp->dm_name, pdp->mod))
- continue;
-
- pd.id = DTRACE_IDNONE;
- pd.prv = pdp->prv;
- pd.mod = smp->dm_name;
- pd.fun = fun;
-
- if (prb & FBT_ENTRY) {
- pd.prb = "entry";
- n += provide_probe(dtp, &pd);
- }
- if (prb & FBT_RETURN) {
- pd.prb = "return";
- n += provide_probe(dtp, &pd);
- }
- }
-
- return n;
-}
-
-/*
- * Generate a BPF trampoline for a FBT probe.
- *
- * The trampoline function is called when a FBT probe triggers, and it must
- * satisfy the following prototype:
- *
- * int dt_rawfbt(dt_pt_regs *regs)
- *
- * The trampoline will populate a dt_dctx_t struct and then call the function
- * that implements the compiled D clause. It returns 0 to the caller.
- */
-static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
-{
- dt_cg_tramp_prologue(pcb);
-
- /*
- * After the dt_cg_tramp_prologue() call, we have:
- * // (%r7 = dctx->mst)
- * // (%r8 = dctx->ctx)
- */
- dt_cg_tramp_copy_regs(pcb);
- if (strcmp(pcb->pcb_probe->desc->prb, "return") == 0) {
- dt_irlist_t *dlp = &pcb->pcb_ir;
-
- dt_cg_tramp_copy_rval_from_regs(pcb);
-
- /*
- * fbt:::return arg0 should be the function offset for
- * return instruction. Since we use kretprobes, however,
- * which do not fire until the function has returned to
- * its caller, information about the returning instruction
- * in the callee has been lost.
- *
- * Set arg0=-1 to indicate that we do not know the value.
- */
- dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, BPF_REG_0, -1);
- emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_0));
- } else
- dt_cg_tramp_copy_args_from_regs(pcb, 1);
- dt_cg_tramp_epilogue(pcb);
-
- return 0;
-}
-
-static int attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd)
-{
- char *prb = NULL;
-
- if (!dt_tp_probe_has_info(prp)) {
- char *fn, *p;
- FILE *f;
- int fd, rc = -1;
-
- /*
- * The tracepoint event we will be creating needs to have a
- * valid name. We use a copy of the probe name, with . -> _
- * conversion.
- */
- prb = strdup(prp->desc->fun);
- for (p = prb; *p; p++) {
- if (*p == '.')
- *p = '_';
- }
-
- /*
- * Register the kprobe with the tracing subsystem. This will
- * create a tracepoint event.
- */
- fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND);
- if (fd == -1)
- goto fail;
-
- rc = dprintf(fd, "%c:" FBT_GROUP_FMT "/%s %s\n",
- prp->desc->prb[0] == 'e' ? 'p' : 'r',
- FBT_GROUP_DATA, prb, prp->desc->fun);
- close(fd);
- if (rc == -1)
- goto fail;
-
- /* create format file name */
- if (asprintf(&fn, "%s" FBT_GROUP_FMT "/%s/format", EVENTSFS,
- FBT_GROUP_DATA, prb) == -1)
- goto fail;
-
- /* open format file */
- f = fopen(fn, "r");
- free(fn);
- if (f == NULL)
- goto fail;
-
- /* read event id from format file */
- rc = dt_tp_probe_info(dtp, f, 0, prp, NULL, NULL);
- fclose(f);
-
- if (rc < 0)
- goto fail;
-
- free(prb);
- }
-
- /* attach BPF program to the probe */
- return dt_tp_probe_attach(dtp, prp, bpf_fd);
-
-fail:
- free(prb);
- return -ENOENT;
-}
-
-/*
- * 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 kprobe 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 void detach(dtrace_hdl_t *dtp, const dt_probe_t *prp)
-{
- int fd;
- char *prb, *p;
-
- if (!dt_tp_probe_has_info(prp))
- return;
-
- dt_tp_probe_detach(dtp, prp);
-
- fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND);
- if (fd == -1)
- return;
-
- /* The tracepoint event is the probe nam, with . -> _ conversion. */
- prb = strdup(prp->desc->fun);
- for (p = prb; *p; p++) {
- if (*p == '.')
- *p = '_';
- }
-
- dprintf(fd, "-:" FBT_GROUP_FMT "/%s\n", FBT_GROUP_DATA, prb);
- free(prb);
- close(fd);
-}
-
-dt_provimpl_t dt_rawfbt = {
- .name = prvname,
- .prog_type = BPF_PROG_TYPE_KPROBE,
- .populate = &populate,
- .provide = &provide,
- .load_prog = &dt_bpf_prog_load,
- .trampoline = &trampoline,
- .attach = &attach,
- .detach = &detach,
- .probe_destroy = &dt_tp_probe_destroy,
-};
--
2.45.2
More information about the DTrace-devel
mailing list