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

Kris Van Hees kris.van.hees at oracle.com
Wed Apr 3 15:26:47 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.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/Build       |  5 +--
 libdtrace/dt_btf.c    | 88 ++++++++++++++++++++++++++++---------------
 libdtrace/dt_btf.h    |  6 ++-
 libdtrace/dt_impl.h   |  3 +-
 libdtrace/dt_module.c | 70 ++++++++++++++++++++++------------
 5 files changed, 111 insertions(+), 61 deletions(-)

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..3f713632 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);
@@ -362,11 +362,11 @@ dt_btf_add_to_ctf(dtrace_hdl_t *dtp, dt_btf_t *btf, ctf_dict_t *ctf,
 	 * If we are not constructing shared_ctf, we may be looking for a type
 	 * in 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_btf && 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 +668,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 +695,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;
@@ -715,13 +718,13 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
 
 	/*
 	 * Any module other than 'vmlinux' inherits the types from 'vmlinux'.
-	 * The shared types are 1 through (base = dtp->dt_btf->type_cnt - 1).
+	 * Shared types are 1 through (base = dtp->dt_shared_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;
+	if (btf != dtp->dt_shared_btf)
+		base = dtp->dt_shared_btf->type_cnt - 1;
 
 	for (i = 1; i < btf->type_cnt; i++) {
 		int	type_id = i + base;
@@ -733,13 +736,14 @@ 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;
 }
 
-ctf_dict_t *
+dt_btf_t *
 dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
 {
 	char		fn[PATH_MAX + 1];
@@ -747,36 +751,59 @@ dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
 
 	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 && 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 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;
+	dt_btf_t	*btf = dmp->dm_btf;
+
+	/* If we already have CTF data, return it. */
+	if (dmp->dm_ctfp)
+		return dmp->dm_ctfp;
+
+	/*
+	 * 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);
 
-	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 +826,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 +843,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 +860,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..85ae315e 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,9 @@ 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 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..3182f388 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_free(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,33 @@ 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);
+
+	dmp->dm_btf = dt_btf_load_module(dtp, dmp);
+
+	/*
+	 * If no module specific BTF was 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 (dmp->dm_btf == NULL)
+		dmp->dm_btf = dtp->dt_shared_btf;
+}
+
 /*
  * Determine the location of a kernel module's CTF data.
  *
@@ -872,18 +903,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 +918,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 +950,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 +960,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",
-- 
2.42.0




More information about the DTrace-devel mailing list