[DTrace-devel] [PATCH v5 02/11] libdtrace: share vmlinux BTF/CTF globally to support faster startup
Kris Van Hees
kris.van.hees at oracle.com
Wed Oct 7 10:05:15 UTC 2026
First of all, this is great work and definitely a major improvement for
start-up time for DTrace.
That said, I am debating with myself whether to take it as is, or to do
more work on this. The point being that while your patch is certainly
an improvement, the code it deals with is in need of a major overhaul.
There are multiple problems with it, the biggest one being that DTrace
provides dtrace_update() as an API function, and its result is a reload
of all modules and their data (including BTF and CTF), but the shared
pointers in dtp are never updated to new versions. In practice that is
thankfully not a problem since the kernel is not getting changed from
underneath us (at least not unfil live update comes along). But it is
wrong either way.
There is also the point that the dtp->dt_shared_ctf and
dtp->dt_shared_btf pointers are just ugly, and shouldn't exist. It is
easy enough to just use the dmp->dm_ctf and dmp->dm_btf pointers from
the vmlinux module if the "shared" CTF or BTF is needed. Since the
shared version is mostly used for BTF because modules' BTF is an
extension of the kernel BTF, introducing a parent BTF pointer in
dt_module_t is probably better.
I had work going on in that area a few months ago, and I'll dig it up to
see if that is sufficiently complete to replace your patch and get the
same overall functionality or whether it needs more work.
Stay tuned...
On Fri, Aug 21, 2026 at 05:10:50PM +0100, Alan Maguire wrote:
> In a multiple DTrace session per process environment, it makes sense
> to avoid doing expensive operations like reading in vmlinux BTF and
> converting it to CTF more than once. Shared (vmlinux) BTF and CTF
> generated from it are shared and reference-counted globally here
> to cut down on cost. With this change, we can launch ~1 DTrace
> session per second.
>
> Note that we need to handle case where -xbtfpath/DTRACE_OPT_BTFPATH
> are used as non-default BTF paths; in such cases global shared
> BTF is not used in the handle.
>
> Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
> ---
> libdtrace/dt_btf.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-
> libdtrace/dt_btf.h | 1 +
> libdtrace/dt_impl.h | 4 +-
> libdtrace/dt_open.c | 5 +--
> 4 files changed, 94 insertions(+), 8 deletions(-)
>
> diff --git a/libdtrace/dt_btf.c b/libdtrace/dt_btf.c
> index 1f793397..e7980969 100644
> --- a/libdtrace/dt_btf.c
> +++ b/libdtrace/dt_btf.c
> @@ -693,6 +693,9 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
> ctf_dict_t *ctf;
> ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 0 };
>
> + if (dmp == NULL && dtp->dt_shared_ctf)
> + return dtp->dt_shared_ctf;
> +
> ctf = ctf_create(&dtp->dt_ctferr);
> if (ctf == NULL)
> return NULL;
> @@ -764,6 +767,81 @@ out:
> }
> #endif
>
> +static dt_btf_t *dt_shared_btf = NULL;
> +#ifdef HAVE_LIBCTF
> +static ctf_dict_t *dt_shared_ctf = NULL;
> +static int dt_shared_ctfmodel = -1;
> +#endif
> +static pthread_mutex_t dt_shared_mutex = PTHREAD_MUTEX_INITIALIZER;
> +static int dt_shared_refcnt = 0;
> +
> +static int
> +dt_shared_get(dtrace_hdl_t *dtp, dt_module_t *dmp)
> +{
> + int rc = -1;
> +
> + pthread_mutex_lock(&dt_shared_mutex);
> + if (!dt_shared_btf)
> + dt_shared_btf = dt_btf_load_file(dtp, "/sys/kernel/btf/vmlinux");
> + if (!dt_shared_btf)
> + goto out;
> +
> +#ifdef HAVE_LIBCTF
> + /* BTF-to-CTF type IDs and CTF data models cannot be shared. */
> + if (dt_shared_ctf && dt_shared_ctfmodel != dtp->dt_conf.dtc_ctfmodel)
> + goto out;
> +
> + if (!dt_shared_ctf) {
> + dt_shared_ctf = dt_btf_to_ctf(dtp, NULL, dt_shared_btf);
> + if (!dt_shared_ctf) {
> + dt_btf_destroy(dtp, dt_shared_btf);
> + dt_shared_btf = NULL;
> + goto out;
> + }
> + dt_shared_ctfmodel = dtp->dt_conf.dtc_ctfmodel;
> + }
> + dtp->dt_shared_ctf = dmp->dm_ctfp = dt_shared_ctf;
> +#endif
> + dtp->dt_shared_btf = dmp->dm_btf = dt_shared_btf;
> + dt_shared_refcnt++;
> + rc = 0;
> +out:
> + pthread_mutex_unlock(&dt_shared_mutex);
> + return rc;
> +}
> +
> +void
> +dt_shared_put(dtrace_hdl_t *dtp)
> +{
> + int shared;
> +
> + pthread_mutex_lock(&dt_shared_mutex);
> + shared = dtp->dt_shared_btf && dtp->dt_shared_btf == dt_shared_btf;
> + if (shared && --dt_shared_refcnt != 0)
> + goto out;
> +
> + dt_btf_destroy(dtp, dtp->dt_shared_btf);
> + dtp->dt_shared_btf = NULL;
> + if (shared)
> + dt_shared_btf = NULL;
> +
> +#ifdef HAVE_LIBCTF
> + if (shared) {
> + if (dt_shared_ctf != NULL)
> + ctf_close(dt_shared_ctf);
> + dt_shared_ctf = NULL;
> + dt_shared_ctfmodel = -1;
> + } else {
> + if (dtp->dt_shared_ctf != NULL)
> + ctf_close(dtp->dt_shared_ctf);
> + }
> + dtp->dt_shared_ctf = NULL;
> +#endif
> +
> +out:
> + pthread_mutex_unlock(&dt_shared_mutex);
> +}
> +
> dt_btf_t *
> dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
> {
> @@ -774,6 +852,13 @@ dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
> if (dmp->dm_btf)
> return dmp->dm_btf;
>
> + /* Share only the system-default vmlinux BTF. */
> + if (dtp->dt_btf_path == NULL &&
> + strcmp(dmp->dm_name, "vmlinux") == 0) {
> + if (dt_shared_get(dtp, dmp) == 0)
> + return dmp->dm_btf;
> + }
> +
> /*
> * Default: /sys/kernel/btf/<module>
> * If "none", disable BTF.
> @@ -899,9 +984,12 @@ dt_btf_lookup_name_kind(dtrace_hdl_t *dtp, dt_module_t *dmp, const char *name,
> if (strcmp(name, "void") == 0)
> return 0;
>
> - /* Ensure the shared BTF is loaded. */
> - if (!dtp->dt_shared_btf)
> + /* Ensure the vmlinux BTF base is loaded before considering a module. */
> + if (!dtp->dt_shared_btf) {
> dt_btf_load_module(dtp, dtp->dt_exec);
> + if (!dtp->dt_shared_btf)
> + return -ENOENT;
> + }
>
> /* If the module does not have BTF data yet, try to load it. */
> if (!btf) {
> diff --git a/libdtrace/dt_btf.h b/libdtrace/dt_btf.h
> index 2c921a03..b1ed2b65 100644
> --- a/libdtrace/dt_btf.h
> +++ b/libdtrace/dt_btf.h
> @@ -18,6 +18,7 @@ typedef struct dt_btf dt_btf_t;
> typedef struct bpf_btf_info btf_info_t;
>
> extern void dt_btf_destroy(dtrace_hdl_t *, dt_btf_t *);
> +extern void dt_shared_put(dtrace_hdl_t *);
> extern dt_btf_t *dt_btf_load_module(dtrace_hdl_t *, dt_module_t *);
> extern ctf_dict_t *dt_btf_module_ctf(dtrace_hdl_t *, dt_module_t *);
> extern const char *dt_btf_get_string(dtrace_hdl_t *, const dt_btf_t *,
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index 7db2ba93..7ccdc271 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -305,8 +305,8 @@ struct dtrace_hdl {
> char *dt_ctfa_path; /* path to vmlinux.ctfa */
> ctf_archive_t *dt_ctfa; /* ctf archive for the entire kernel tree */
> char *dt_btf_path; /* path to vmlinux.btf */
> - struct dt_btf *dt_shared_btf; /* BTF data for the kernel (shared) */
> - ctf_file_t *dt_shared_ctf; /* Handle to the shared CTF */
> + struct dt_btf *dt_shared_btf; /* BTF data shared by kernel modules */
> + ctf_file_t *dt_shared_ctf; /* CTF shared by kernel modules */
> dt_htab_t *dt_kernpaths; /* hash table of dt_kern_path_t's */
> dt_module_t *dt_exec; /* pointer to executable module */
> dt_module_t *dt_cdefs; /* pointer to C dynamic type module */
> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> index 30b8758d..352cec49 100644
> --- a/libdtrace/dt_open.c
> +++ b/libdtrace/dt_open.c
> @@ -1299,10 +1299,7 @@ dtrace_close(dtrace_hdl_t *dtp)
> dt_htab_destroy(dtp->dt_mods);
> dt_htab_destroy(dtp->dt_kernpaths);
>
> - if (dtp->dt_shared_btf != NULL)
> - dt_btf_destroy(dtp, dtp->dt_shared_btf);
> - if (dtp->dt_shared_ctf != NULL)
> - ctf_close(dtp->dt_shared_ctf);
> + dt_shared_put(dtp);
> if (dtp->dt_ctfa != NULL)
> ctf_arc_close(dtp->dt_ctfa);
>
> --
> 2.43.5
>
More information about the DTrace-devel
mailing list