[DTrace-devel] [PATCH v2 2/8] btf: always load BTF (if available)

Kris Van Hees kris.van.hees at oracle.com
Fri Apr 12 14:57:45 UTC 2024


Upcoming work will make use of BPF features that depend on BTF data.
Load it if available, and generate CTF from it if no CTF archive is
found.

This patch also fixes several memory leaks in the BTF handling code.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 include/sys/ctf-api.h |   1 +
 libdtrace/Build       |   5 +-
 libdtrace/dt_btf.c    | 170 ++++++++++++++++++++++++++++--------------
 libdtrace/dt_btf.h    |   7 +-
 libdtrace/dt_impl.h   |   3 +-
 libdtrace/dt_module.c |  68 +++++++++++------
 libdtrace/dt_open.c   |   3 +
 7 files changed, 168 insertions(+), 89 deletions(-)

diff --git a/include/sys/ctf-api.h b/include/sys/ctf-api.h
index 5710584b..a8e2b84b 100644
--- a/include/sys/ctf-api.h
+++ b/include/sys/ctf-api.h
@@ -15,6 +15,7 @@
 # define ctf_close(fp)	ctf_file_close(fp)
 #else
 # include <sys/ctf_api.h>
+typedef ctf_file_t	ctf_dict_t;
 #endif
 
 #endif /* _DT_CTF_API_H */
diff --git a/libdtrace/Build b/libdtrace/Build
index 4ed04fdf..9733a17b 100644
--- a/libdtrace/Build
+++ b/libdtrace/Build
@@ -13,6 +13,7 @@ libdtrace-build_DIR := $(current-dir)
 libdtrace-build_SOURCES = dt_aggregate.c \
 			  dt_as.c \
 			  dt_bpf.c \
+			  dt_btf.c \
 			  dt_buf.c \
 			  dt_cc.c \
 			  dt_cg.c \
@@ -71,10 +72,6 @@ libdtrace-build_SOURCES = dt_aggregate.c \
 			  dt_work.c \
 			  dt_xlator.c
 
-ifdef HAVE_LIBCTF
-libdtrace-build_SOURCES += dt_btf.c
-endif
-
 libdtrace-build_SRCDEPS := dt_grammar.h $(objdir)/dt_git_version.h
 
 SHLIBS += libdtrace
diff --git a/libdtrace/dt_btf.c b/libdtrace/dt_btf.c
index 59126dd1..087758e6 100644
--- a/libdtrace/dt_btf.c
+++ b/libdtrace/dt_btf.c
@@ -37,7 +37,7 @@ struct dt_btf {
 	ctf_id_t	*ctfids;		/* matching CTF type ids */
 };
 
-static const char *
+const char *
 dt_btf_errmsg(int err)
 {
 	return strerror(err);
@@ -68,20 +68,6 @@ dt_btf_error(dtrace_hdl_t *dtp, int eid, const char *format, ...)
 	va_end(ap);
 }
 
-static ctf_id_t
-dt_ctf_set_errno(dtrace_hdl_t *dtp, int err)
-{
-	dtp->dt_ctferr = err;
-	dt_set_errno(dtp, EDT_CTF);
-	return CTF_ERR;
-}
-
-static ctf_id_t
-dt_ctf_error(dtrace_hdl_t *dtp, ctf_dict_t *ctf)
-{
-	return dt_ctf_set_errno(dtp, ctf_errno(ctf));
-}
-
 static int
 dt_btf_validate_header(dtrace_hdl_t *dtp, dt_btf_t *btf)
 {
@@ -203,6 +189,8 @@ dt_btf_decode(dtrace_hdl_t *dtp, dt_btf_t *btf)
 	/* Next, populate the type offsets table. */
 	btf->type_cnt = idx;
 	btf->types = dt_calloc(dtp, btf->type_cnt, sizeof(btf_type_t *));
+	if (btf->types == NULL)
+		return dt_btf_set_errno(dtp,  ENOMEM);
 
 	ptr = tdata;
 	idx = 1;
@@ -240,7 +228,7 @@ dt_btf_load(dtrace_hdl_t *dtp, const char *fn)
 
 	fp = fopen(fn, "rb");
 	if (fp == NULL)
-		return dt_btf_set_load_errno(dtp,  errno);
+		goto err;
 
 	/* First see whether this might be a file with raw BTF data. */
 	if (fread(&hdr, 1, sizeof(hdr), fp) < sizeof(hdr))
@@ -315,14 +303,14 @@ elf_fail:
 elf_fail_no_end:
 	err = elf_errno();
 	dt_btf_error(dtp, 0, "BTF: %s", elf_errmsg(err));
-	fclose(fp);
-
-	return NULL;
 
 fail:
+	fclose(fp);
 	dt_free(dtp, data);
+
+err:
+	dt_free(dtp, btf);
 	dt_btf_set_errno(dtp,  err ? err : errno);
-	fclose(fp);
 
 	return NULL;
 }
@@ -342,12 +330,40 @@ dt_btf_load_file(dtrace_hdl_t *dtp, const char *fn)
 	if (dt_btf_decode(dtp, btf) == -1) {
 		dt_dprintf("Cannot decode BTF data %s: %s\n", fn,
 			   dt_btf_errmsg(dtp->dt_btferr));
+		dt_btf_destroy(dtp, btf);
 		return NULL;
 	}
 
 	return btf;
 }
 
+void
+dt_btf_destroy(dtrace_hdl_t *dtp, dt_btf_t *btf)
+{
+	if (!btf)
+		return;
+
+	dt_free(dtp, btf->data);
+	dt_free(dtp, btf->types);
+	dt_free(dtp, btf->ctfids);
+	dt_free(dtp, btf);
+}
+
+#ifdef HAVE_LIBCTF
+static ctf_id_t
+dt_ctf_set_errno(dtrace_hdl_t *dtp, int err)
+{
+	dtp->dt_ctferr = err;
+	dt_set_errno(dtp, EDT_CTF);
+	return CTF_ERR;
+}
+
+static ctf_id_t
+dt_ctf_error(dtrace_hdl_t *dtp, ctf_dict_t *ctf)
+{
+	return dt_ctf_set_errno(dtp, ctf_errno(ctf));
+}
+
 static ctf_id_t
 dt_btf_add_to_ctf(dtrace_hdl_t *dtp, dt_btf_t *btf, ctf_dict_t *ctf,
 		  int32_t type_id)
@@ -359,14 +375,14 @@ dt_btf_add_to_ctf(dtrace_hdl_t *dtp, dt_btf_t *btf, ctf_dict_t *ctf,
 	ctf_encoding_t	enc;
 
 	/*
-	 * If we are not constructing shared_ctf, we may be looking for a type
-	 * in shared_ctf.
+	 * If we already have shared_ctf, we may be looking for a type in that
+	 * shared_ctf.
 	 */
-	if (dtp->dt_btf && btf != dtp->dt_btf) {
-		if (type_id < dtp->dt_btf->type_cnt)
-			return dtp->dt_btf->ctfids[type_id];
+	if (dtp->dt_shared_ctf && btf != dtp->dt_shared_btf) {
+		if (type_id < dtp->dt_shared_btf->type_cnt)
+			return dtp->dt_shared_btf->ctfids[type_id];
 
-		type_id -= dtp->dt_btf->type_cnt - 1;
+		type_id -= dtp->dt_shared_btf->type_cnt - 1;
 	}
 
 	assert(type_id < btf->type_cnt);
@@ -668,7 +684,7 @@ dt_btf_add_to_ctf(dtrace_hdl_t *dtp, dt_btf_t *btf, ctf_dict_t *ctf,
 	assert(0);
 }
 
-ctf_dict_t *
+static ctf_dict_t *
 dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
 {
 	int		i, base = 0;
@@ -695,9 +711,12 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
 		ctf_setspecific(ctf, dmp);
 
 		if (strcmp(dmp->dm_name, "vmlinux") == 0)
-			return ctf;
+			goto out;
 	}
 
+	if (!btf)
+		goto out;
+
 	btf->ctfids = dt_calloc(dtp, btf->type_cnt, sizeof(ctf_id_t));
 	for (i = 1; i < btf->type_cnt; i++)
 		btf->ctfids[i] = CTF_ERR;
@@ -711,18 +730,20 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
 						 &enc);
 		if (btf->ctfids[0] == CTF_ERR)
 			dt_dprintf("Could not create 'void' CTF entry.\n");
+	} else {
+		/*
+		 * Any module other than 'vmlinux' inherits the types from
+		 * 'vmlinux'.
+		 * Shared types are 1 through dt_shared_btf->type_cnt - 1
+		 * (base).
+		 * A module's types are base through base + btf->type_cnt - 1,
+		 * but the types are stored in the BTF types array with indexes
+		 * 1 through btf->type_cnt - 1.
+		 */
+		if (btf != dtp->dt_shared_btf)
+			base = dtp->dt_shared_btf->type_cnt - 1;
 	}
 
-	/*
-	 * Any module other than 'vmlinux' inherits the types from 'vmlinux'.
-	 * The shared types are 1 through (base = dtp->dt_btf->type_cnt - 1).
-	 * A module's types are base through (base + btf->type_cnt - 1), but
-	 * the types are stored in the BTF types array with indexes 1 through
-	 * (btf->type_cnt - 1).
-	 */
-	if (dtp->dt_btf)
-		base = dtp->dt_btf->type_cnt - 1;
-
 	for (i = 1; i < btf->type_cnt; i++) {
 		int	type_id = i + base;
 
@@ -733,50 +754,82 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
 		}
 	}
 
+out:
 	if (ctf_update(ctf) == CTF_ERR)
 		return NULL;
 
 	return ctf;
 }
+#endif
 
-ctf_dict_t *
+dt_btf_t *
 dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
 {
 	char		fn[PATH_MAX + 1];
 	dt_btf_t	*btf;
 
+	if (dmp->dm_btf)
+		return dmp->dm_btf;
+
 	snprintf(fn, sizeof(fn), "/sys/kernel/btf/%s", dmp->dm_name);
 	btf = dt_btf_load_file(dtp, fn);
-	if (btf == NULL)
-		return NULL;
 
-	if (strcmp(dmp->dm_name, "vmlinux") == 0) {
-		dtp->dt_shared_ctf = dt_btf_to_ctf(dtp, NULL, btf);
-		dtp->dt_btf = btf;
+	if (btf && !dtp->dt_shared_btf && strcmp(dmp->dm_name, "vmlinux") == 0)
+		dtp->dt_shared_btf = btf;
 
-		dt_dprintf("Generated shared CTF from BTF (%d types).\n",
-			   btf->type_cnt);
+	return dmp->dm_btf = btf;
+}
 
-		return dtp->dt_shared_ctf;
-	}
+ctf_dict_t *
+dt_btf_module_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
+{
+	ctf_dict_t	*ctf = NULL;
+#ifdef HAVE_LIBCTF
+	dt_btf_t	*btf = dmp->dm_btf;
+#endif
+
+	/* If we already have CTF data, return it. */
+	if (dmp->dm_ctfp)
+		return dmp->dm_ctfp;
+
+#ifdef HAVE_LIBCTF
+	/*
+	 * If the module is 'vmlinux', we are creating the shared CTF.
+	 * If the module uses the shared BTF, we create CTF for a NULL BTF.
+	 * Otherwise, convert the module BTF.
+	 */
+	if (strcmp(dmp->dm_name, "vmlinux") == 0)
+		ctf = dt_btf_to_ctf(dtp, NULL, btf);
+	else if (btf == dtp->dt_shared_btf)
+		ctf = dt_btf_to_ctf(dtp, dmp, NULL);
+	else
+		ctf = dt_btf_to_ctf(dtp, dmp, btf);
+
+	/*
+	 * If generating CTF data for the main kernel, store it as the shared
+	 * CTF.
+	 */
+	if (!dtp->dt_shared_ctf && dtp->dt_shared_btf && btf == dtp->dt_shared_btf)
+		dtp->dt_shared_ctf = ctf;
 
 	dt_dprintf("Generated %s CTF from BTF (%d types).\n", dmp->dm_name,
 		   btf->type_cnt);
+#endif
 
-	return dt_btf_to_ctf(dtp, dmp, btf);
+	return ctf;
 }
 
 const char *
 dt_btf_get_string(dtrace_hdl_t *dtp, dt_btf_t *btf, uint32_t off)
 {
-	if (dtp->dt_btf == NULL)
+	if (dtp->dt_shared_btf == NULL)
 		goto ok;
 
 	/* Check if the offset is within the base BTF string area. */
-	if (btf == dtp->dt_btf || off < dtp->dt_btf->hdr->str_len)
-		return dtp->dt_btf->sdata + off;
+	if (btf == dtp->dt_shared_btf || off < dtp->dt_shared_btf->hdr->str_len)
+		return dtp->dt_shared_btf->sdata + off;
 
-	off -= dtp->dt_btf->hdr->str_len;
+	off -= dtp->dt_shared_btf->hdr->str_len;
 ok:
 	if (off < btf->hdr->str_len)
 		return btf->sdata + off;
@@ -799,11 +852,11 @@ dt_btf_lookup_name_kind(dtrace_hdl_t *dtp, dt_btf_t *btf, const char *name,
 	 * Ensure the shared BTF is loaded, and if no BTF is given, use the
 	 * shared one.
 	 */
-	 if (!dtp->dt_btf) {
+	 if (!dtp->dt_shared_btf) {
 		  dt_btf_load_module(dtp, dtp->dt_exec);
 
 		  if (!btf)
-			   btf = dtp->dt_btf;
+			   btf = dtp->dt_shared_btf;
 	 }
 
 	 if (!btf)
@@ -816,8 +869,8 @@ dt_btf_lookup_name_kind(dtrace_hdl_t *dtp, dt_btf_t *btf, const char *name,
 	 * the types are stored in the BTF types array with indexes 1 through
 	 * (btf->type_cnt - 1).
 	 */
-	if (btf != dtp->dt_btf)
-		base = dtp->dt_btf->type_cnt - 1;
+	if (btf != dtp->dt_shared_btf)
+		base = dtp->dt_shared_btf->type_cnt - 1;
 
 
 	for (i = 1; i < btf->type_cnt; i++) {
@@ -833,7 +886,8 @@ dt_btf_lookup_name_kind(dtrace_hdl_t *dtp, dt_btf_t *btf, const char *name,
 	}
 
 	if (base > 0)
-		return dt_btf_lookup_name_kind(dtp, dtp->dt_btf, name, kind);
+		return dt_btf_lookup_name_kind(dtp, dtp->dt_shared_btf,
+					       name, kind);
 
 	return -ENOENT;
 }
diff --git a/libdtrace/dt_btf.h b/libdtrace/dt_btf.h
index 863fa470..ac9c5602 100644
--- a/libdtrace/dt_btf.h
+++ b/libdtrace/dt_btf.h
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 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.
  */
@@ -16,7 +16,10 @@ extern "C" {
 
 typedef struct dt_btf	dt_btf_t;
 
-extern ctf_dict_t *dt_btf_load_module(dtrace_hdl_t *, dt_module_t *);
+extern const char *dt_btf_errmsg(int);
+extern void dt_btf_destroy(dtrace_hdl_t *, dt_btf_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 *, dt_btf_t *, uint32_t);
 
 #ifdef	__cplusplus
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 2dfb64d6..3f258376 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -159,6 +159,7 @@ typedef struct dt_module {
 	ctf_sect_t dm_ctdata;	/* CTF data for module */
 	ctf_sect_t dm_symtab;	/* symbol table */
 	ctf_sect_t dm_strtab;	/* string table */
+	struct dt_btf *dm_btf;	/* BTF data for module */
 
 	/*
 	 * Kernel modules only.
@@ -337,7 +338,7 @@ struct dtrace_hdl {
 	dt_htab_t *dt_kernsyms; /* htab of kernel symbol names */
 	char *dt_ctfa_path;	/* path to vmlinux.ctfa */
 	ctf_archive_t *dt_ctfa; /* ctf archive for the entire kernel tree */
-	struct dt_btf *dt_btf;	/* BTF data for the kernel */
+	struct dt_btf *dt_shared_btf; /* BTF data for the kernel (shared) */
 	ctf_file_t *dt_shared_ctf; /* Handle to the shared CTF */
 	dt_htab_t *dt_kernpaths; /* hash table of dt_kern_path_t's */
 	dt_module_t *dt_exec;	/* pointer to executable module */
diff --git a/libdtrace/dt_module.c b/libdtrace/dt_module.c
index 1e730426..cf149926 100644
--- a/libdtrace/dt_module.c
+++ b/libdtrace/dt_module.c
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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.
  */
@@ -41,6 +41,9 @@ dt_module_unload(dtrace_hdl_t *dtp, dt_module_t *dmp);
 static void
 dt_module_shuffle_to_start(dtrace_hdl_t *dtp, const char *name);
 
+static void
+dt_kern_module_find_btf(dtrace_hdl_t *dtp, dt_module_t *dmp);
+
 static void
 dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp);
 
@@ -392,6 +395,9 @@ dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp)
 	if (dmp->dm_flags & DT_DM_LOADED)
 		return 0; /* module is already loaded */
 
+	/* Load BTF data for the module. */
+	dt_kern_module_find_btf(dtp, dmp);
+
 	/*
 	 * First find out where the module is, and preliminarily load its CTF.
 	 * If this fails, we don't care: the problem will be detected in
@@ -610,6 +616,10 @@ dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 static void
 dt_module_unload(dtrace_hdl_t *dtp, dt_module_t *dmp)
 {
+	if (dmp->dm_btf != dtp->dt_shared_btf)
+		dt_btf_destroy(dtp, dmp->dm_btf);
+	dmp->dm_btf = NULL;
+
 	if (dmp->dm_ctfp != dtp->dt_shared_ctf)
 		ctf_close(dmp->dm_ctfp);
 	dmp->dm_ctfp = NULL;
@@ -834,17 +844,11 @@ dt_kern_module_init(dtrace_hdl_t *dtp, dt_module_t *dmp)
 static void
 dt_kern_module_ctf_from_btf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 {
-	/*
-	 * The first module for which we need to collect data must be the
-	 * 'vmlinux' module.  It will be used to initialize the shared_ctf
-	 * data.
-	 */
-	if (dtp->dt_btf == NULL)
-		assert(strcmp(dmp->dm_name, "vmlinux") == 0);
+	assert(dmp->dm_btf != NULL);
 
 	dt_dprintf("Generating CTF for module %s from BTF.\n", dmp->dm_name);
 
-	dmp->dm_ctfp = dt_btf_load_module(dtp, dmp);
+	dmp->dm_ctfp = dt_btf_module_ctf(dtp, dmp);
 	if (dmp->dm_ctfp == NULL)
 		dt_dprintf("Cannot generate CTF for module %s from BTF: %s; "
 			   "looking for out-of-tree module.\n",
@@ -854,6 +858,31 @@ dt_kern_module_ctf_from_btf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 }
 #endif
 
+/*
+ * Load BTF data for a kernel module (vmlinux is treated as its own module as
+ * well), if available and not already loaded.
+ */
+static void
+dt_kern_module_find_btf(dtrace_hdl_t *dtp, dt_module_t *dmp)
+{
+	/*
+	 * The first module for which we need to collect BTF data must be the
+	 * 'vmlinux' module.
+	 */
+	if (dtp->dt_shared_btf == NULL)
+		assert(strcmp(dmp->dm_name, "vmlinux") == 0);
+
+	dt_dprintf("Loading BTF for module %s.\n", dmp->dm_name);
+
+	/*
+	 * If no module specific BTF is found, we assume it is a builtin
+	 * module and we assign the shared BTF to it.  That is the fallback
+	 * anyway for type lookups in modules, so it is safe.
+	 */
+	if (dt_btf_load_module(dtp, dmp) == NULL)
+		dmp->dm_btf = dtp->dt_shared_btf;
+}
+
 /*
  * Determine the location of a kernel module's CTF data.
  *
@@ -872,18 +901,6 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 		dt_kern_module_init(dtp, dmp);
 	}
 
-#ifdef HAVE_LIBCTF
-	/*
-	 * If we already know that our CTF data is derived from BTF data (i.e.
-	 * there is no CTF archive for the runtime kernel), there is no point
-	 * in trying to load CTF data.  Go directly to BTF-based processing.
-	 */
-	if (dtp->dt_btf) {
-		dt_kern_module_ctf_from_btf(dtp, dmp);
-		return;
-	}
-#endif
-
 	/*
 	 * Kernel modules' CTF can be in one of two places: the CTF archive or
 	 * linked into the module (for out-of-tree modules).  The corresponding
@@ -899,7 +916,7 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 	 *
 	 * Check for a CTF archive containing the specified module.
 	 */
-	if (dtp->dt_ctfa == NULL) {
+	if (dtp->dt_ctfa == NULL && dtp->dt_shared_ctf == NULL) {
 		char *ctfa_name;
 		char *to;
 
@@ -931,6 +948,8 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 			}
 		} else {
 #ifdef HAVE_LIBCTF
+			dt_module_t	*mod;
+
 			dt_dprintf("Cannot open CTF archive %s: %s; "
 				   "trying BTF.\n",
 				   ctfa_name, ctf_errmsg(dtp->dt_ctferr));
@@ -939,8 +958,9 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
 			 * Try to load the vmlinux BTF data to generate the CTF
 			 * data for shared_ctf from.
 			 */
-			dt_kern_module_ctf_from_btf(dtp,
-				dt_module_lookup_by_name(dtp, "vmlinux"));
+			mod = dt_module_lookup_by_name(dtp, "vmlinux");
+			dt_kern_module_ctf_from_btf(dtp, mod);
+			dtp->dt_shared_ctf = mod->dm_ctfp;
 #else
 			dt_dprintf("Cannot open CTF archive %s: %s; "
 				   "looking for in-module CTF instead.\n",
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index 53db0d6d..091cf4b5 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -29,6 +29,7 @@
 
 #include <dt_impl.h>
 #include <dt_bpf.h>
+#include <dt_btf.h>
 #include <dt_pcap.h>
 #include <dt_program.h>
 #include <dt_module.h>
@@ -1279,6 +1280,8 @@ dtrace_close(dtrace_hdl_t *dtp)
 	dt_htab_destroy(dtp, dtp->dt_mods);
 	dt_htab_destroy(dtp, 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);
 	if (dtp->dt_ctfa != NULL)
-- 
2.42.0




More information about the DTrace-devel mailing list