[DTrace-devel] [PATCH v3] ctf: add BTF-to-CTF convertor
Alan Maguire
alan.maguire at oracle.com
Fri Nov 10 18:17:03 UTC 2023
On 08/11/2023 17:45, Kris Van Hees via DTrace-devel wrote:
> For kernels that do not provide CTF data, kernel type information can be
> obtained at runtime as BTF data. We pull in that data and generate
> equivalent CTF data from it.
>
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
> Reviewed-by: Nick Alcock <nick.alcock at oracle.com>
just tested this on an upstream bpf-next kernel, works beautifully:
# /usr/sbin/dtrace -n 'fbt::ip_rcv_core:entry { printf("%s\n",
stringof(((struct sk_buff *)arg0)->dev)); }'
dtrace: description 'fbt::ip_rcv_core:entry ' matched 1 probe
CPU ID FUNCTION:NAME
0 80242 ip_rcv_core:entry wlo1
3 80242 ip_rcv_core:entry cscotun0
Great stuff!
> ---
> libdtrace/Build | 1 +
> libdtrace/dt_btf.c | 811 ++++++++++++++++++++++++++++++++++++++++++
> libdtrace/dt_btf.h | 27 ++
> libdtrace/dt_impl.h | 4 +-
> libdtrace/dt_module.c | 57 ++-
> 5 files changed, 890 insertions(+), 10 deletions(-)
> create mode 100644 libdtrace/dt_btf.c
> create mode 100644 libdtrace/dt_btf.h
>
> diff --git a/libdtrace/Build b/libdtrace/Build
> index 35c0fd4a..76fd32e4 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 \
> diff --git a/libdtrace/dt_btf.c b/libdtrace/dt_btf.c
> new file mode 100644
> index 00000000..2cb26aa5
> --- /dev/null
> +++ b/libdtrace/dt_btf.c
> @@ -0,0 +1,811 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +#include <assert.h>
> +#include <byteswap.h>
> +#include <ctf.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <sys/mman.h>
> +#include <linux/btf.h>
> +
> +#include <dt_impl.h>
> +#include <dt_btf.h>
> +
> +typedef struct btf_header btf_header_t;
> +typedef struct btf_type btf_type_t;
> +typedef struct btf_enum btf_enum_t;
> +typedef struct btf_array btf_array_t;
> +typedef struct btf_member btf_member_t;
> +typedef struct btf_param btf_param_t;
> +typedef struct btf_var btf_var_t;
> +typedef struct btf_var_secinfo btf_var_secinfo_t;
> +typedef struct btf_decl_tag btf_decl_tag_t;
> +typedef struct btf_enum64 btf_enum64_t;
> +
> +struct dt_btf {
> + void *data; /* raw BTF data */
> + size_t size; /* raw BTF data size */
> + btf_header_t *hdr; /* BTF header */
> + char *sdata; /* string data */
> + uint32_t ssize; /* string data size */
> + int32_t type_cnt; /* number of types */
> + btf_type_t **types; /* type table */
> + ctf_id_t *ctfids; /* matching CTF type ids */
> +};
> +
> +static const char *
> +dt_btf_errmsg(int err)
> +{
> + return strerror(err);
> +}
> +
> +static int
> +dt_btf_set_errno(dtrace_hdl_t *dtp, int err)
> +{
> + dtp->dt_btferr = err;
> + return -1;
> +}
> +
> +static void *
> +dt_btf_set_load_errno(dtrace_hdl_t *dtp, int err)
> +{
> + dtp->dt_btferr = err;
> + return NULL;
> +}
> +
> +static void
> +dt_btf_error(dtrace_hdl_t *dtp, int eid, const char *format, ...)
> +{
> + va_list ap;
> +
> + va_start(ap, format);
> + dt_set_errmsg(dtp, dt_errtag(eid), NULL, NULL, 0, format, ap);
> + dt_set_errno(dtp, EDT_BPF);
> + 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)
> +{
> + btf_header_t *hdr = (btf_header_t *)btf->data;
> +
> + btf->hdr = hdr;
> +
> + /* Validate magic number. */
> + if (hdr->magic != BTF_MAGIC) {
> + dt_dprintf("BTF magic mismatch (%04x, expected %04x)\n",
> + hdr->magic, BTF_MAGIC);
> + return dt_btf_set_errno(dtp, EINVAL);
> + }
> +
> + /* Validate header length. */
> + if (hdr->hdr_len < sizeof(btf_header_t) ||
> + hdr->hdr_len >= btf->size) {
> + dt_dprintf("BTF bad header length (%d)\n", hdr->hdr_len);
> + return dt_btf_set_errno(dtp, EINVAL);
> + }
> +
> + /* Validate data offsets and lengths. */
> + if (hdr->type_off % 4) {
> + dt_dprintf("BTF type offset misaligned (%d)\n", hdr->type_off);
> + return dt_btf_set_errno(dtp, EINVAL);
> + }
> + if (hdr->type_off + hdr->type_len > hdr->str_off) {
> + dt_dprintf("BTF bad type section length (%d)\n", hdr->type_len);
> + return dt_btf_set_errno(dtp, EINVAL);
> + }
> + if (hdr->hdr_len + hdr->str_off + hdr->str_len > btf->size) {
> + dt_dprintf("BTF bad string section length (%d)\n",
> + hdr->str_len);
> + return dt_btf_set_errno(dtp, EINVAL);
> + }
> +
> + return 0;
> +}
> +
> +static size_t
> +dt_btf_type_size(const char *ptr)
> +{
> + btf_type_t *type = (btf_type_t *)ptr;
> + size_t size = sizeof(btf_type_t);
> + int vlen = BTF_INFO_VLEN(type->info);
> +
> + switch (BTF_INFO_KIND(type->info)) {
> + case BTF_KIND_INT:
> + return size + sizeof(uint32_t);
> + case BTF_KIND_ARRAY:
> + return size + sizeof(btf_array_t);
> + case BTF_KIND_STRUCT:
> + case BTF_KIND_UNION:
> + return size + vlen * sizeof(btf_member_t);
> + case BTF_KIND_ENUM:
> + return size + vlen * sizeof(btf_enum_t);
> + case BTF_KIND_FUNC_PROTO:
> + return size + vlen * sizeof(btf_param_t);
> + case BTF_KIND_VAR:
> + return size + sizeof(btf_var_t);
> + case BTF_KIND_DATASEC:
> + return size + vlen * sizeof(btf_var_secinfo_t);
> + case BTF_KIND_DECL_TAG:
> + return size + sizeof(btf_decl_tag_t);
> + case BTF_KIND_ENUM64:
> + return size + vlen * sizeof(btf_enum64_t);
> + case BTF_KIND_PTR:
> + case BTF_KIND_FWD:
> + case BTF_KIND_TYPEDEF:
> + case BTF_KIND_VOLATILE:
> + case BTF_KIND_CONST:
> + case BTF_KIND_RESTRICT:
> + case BTF_KIND_FUNC:
> + case BTF_KIND_FLOAT:
> + case BTF_KIND_TYPE_TAG:
> + return size;
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int
> +dt_btf_decode(dtrace_hdl_t *dtp, dt_btf_t *btf)
> +{
> + btf_header_t *hdr;
> + char *ptr, *end;
> + int32_t idx;
> + char *tdata;
> +
> + if (dt_btf_validate_header(dtp, btf) == -1)
> + return -1;
> +
> + hdr = btf->hdr;
> + btf->sdata = (char *)btf->data + hdr->hdr_len + hdr->str_off;
> + tdata = (char *)btf->data + hdr->hdr_len + hdr->type_off;
> +
> + /* First count how many types there really are. */
> + ptr = tdata;
> + end = tdata + hdr->type_len;
> + idx = 1;
> + while (ptr + sizeof(btf_type_t) <= end) {
> + int size = dt_btf_type_size(ptr);
> + btf_type_t *type = (btf_type_t *)ptr;
> +
> + if (size < 0)
> + return dt_btf_set_errno(dtp, -size);
> + if (ptr + size > end)
> + return dt_btf_set_errno(dtp, EINVAL);
> + if (dt_btf_get_string(dtp, btf, type->name_off) == NULL)
> + return dt_btf_set_errno(dtp, EINVAL);
> +
> + idx++;
> + ptr += size;
> + }
> +
> + /* Next, populate the type offsets table. */
> + btf->type_cnt = idx;
> + btf->types = dt_calloc(dtp, btf->type_cnt, sizeof(btf_type_t *));
> +
> + ptr = tdata;
> + idx = 1;
> + while (ptr + sizeof(btf_type_t) <= end) {
> + int size = dt_btf_type_size(ptr);
> +
> + btf->types[idx++] = (btf_type_t *)ptr;
> + ptr += size;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Try to load BTF data from the given file. We use FILE-based IO primitives
> + * instead of fd-based IO primitives because the BTF data is usually provided
> + * by sysfs pseudo-files and the fd-based IO primitives do not seem to work
> + * with that.
> + */
> +static dt_btf_t *
> +dt_btf_load(dtrace_hdl_t *dtp, const char *fn)
> +{
> + FILE *fp;
> + dt_btf_t *btf = NULL;
> + btf_header_t hdr;
> + void *data = NULL;
> + Elf *elf;
> + size_t shstrs;
> + Elf_Scn *sp = NULL;
> + int err = 0;
> +
> + btf = dt_zalloc(dtp, sizeof(dt_btf_t));
> + if (btf == NULL)
> + return dt_btf_set_load_errno(dtp, ENOMEM);
> +
> + fp = fopen(fn, "rb");
> + if (fp == NULL)
> + return dt_btf_set_load_errno(dtp, errno);
> +
> + /* First see whether this might be a file with raw BTF data. */
> + if (fread(&hdr, 1, sizeof(hdr), fp) < sizeof(hdr))
> + goto fail;
> +
> + rewind(fp);
> + if (hdr.magic == BTF_MAGIC) {
> + struct stat st;
> +
> + if (fstat(fileno(fp), &st) == -1)
> + goto fail;
> +
> + data = dt_alloc(dtp, st.st_size);
> + if (data == NULL)
> + goto fail;
> +
> + if (fread(data, 1, st.st_size, fp) < st.st_size)
> + goto fail;
> +
> + fclose(fp);
> +
> + btf->data = data;
> + btf->size = st.st_size;
> + } else {
> + /* Next see whether this might be an ELF file with BTF data. */
> + elf = elf_begin(fileno(fp), ELF_C_READ_MMAP, NULL);
> + if (elf == NULL)
> + goto elf_fail_no_end;
> + if (elf_getshdrstrndx(elf, &shstrs) == -1)
> + goto elf_fail;
> +
> + while ((sp = elf_nextscn(elf, sp)) != NULL) {
> + GElf_Shdr sh;
> + Elf_Data *edata;
> + const char *name;
> +
> + /* Skip malformed sections. */
> + if (gelf_getshdr(sp, &sh) == NULL ||
> + sh.sh_type == SHT_NULL)
> + continue;
> +
> + name = elf_strptr(elf, shstrs, sh.sh_name);
> + if (name == NULL || strcmp(name, ".BTF") != 0)
> + continue;
> +
> + /* Found the section we need. */
> + edata = elf_getdata(sp, 0);
> + if (edata == NULL)
> + goto elf_fail;
> +
> + data = dt_alloc(dtp, edata->d_size);
> + if (data == NULL)
> + goto elf_fail;
> +
> + memcpy(data, edata->d_buf, edata->d_size);
> +
> + btf->data = data;
> + btf->size = edata->d_size;
> +
> + elf_end(elf);
> + fclose(fp);
> +
> + break;
> + }
> + }
> +
> + return btf;
> +
> +elf_fail:
> + elf_end(elf);
> +
> +elf_fail_no_end:
> + err = elf_errno();
> + dt_btf_error(dtp, 0, "BTF: %s", elf_errmsg(err));
> + fclose(fp);
> +
> + return NULL;
> +
> +fail:
> + dt_free(dtp, data);
> + dt_btf_set_errno(dtp, err ? err : errno);
> + fclose(fp);
> +
> + return NULL;
> +}
> +
> +dt_btf_t *
> +dt_btf_load_file(dtrace_hdl_t *dtp, const char *fn)
> +{
> + dt_btf_t *btf;
> +
> + btf = dt_btf_load(dtp, fn);
> + if (btf == NULL) {
> + dt_dprintf("Cannot open BTF file %s: %s\n", fn,
> + dt_btf_errmsg(dtp->dt_btferr));
> + return NULL;
> + }
> +
> + if (dt_btf_decode(dtp, btf) == -1) {
> + dt_dprintf("Cannot decode BTF data %s: %s\n", fn,
> + dt_btf_errmsg(dtp->dt_btferr));
> + return NULL;
> + }
> +
> + return btf;
> +}
> +
> +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)
> +{
> + btf_type_t *type;
> + int kind, vlen;
> + const char *name;
> + ctf_id_t ctfid;
> + ctf_encoding_t enc;
> +
> + /*
> + * 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];
> +
> + type_id -= dtp->dt_btf->type_cnt - 1;
> + }
> +
> + assert(type_id < btf->type_cnt);
> +
> + if (btf->ctfids[type_id] != CTF_ERR)
> + return btf->ctfids[type_id];
> +
> + assert(type_id > 0);
> +
> + type = btf->types[type_id];
> + kind = BTF_INFO_KIND(type->info);
> + vlen = BTF_INFO_VLEN(type->info);
> + name = dt_btf_get_string(dtp, btf, type->name_off);
> +
> + if (name && name[0]) {
> + char n[DT_TYPE_NAMELEN];
> +
> + /* Do we already have this type? */
> + switch (kind) {
> + case BTF_KIND_ENUM:
> + case BTF_KIND_ENUM64:
> + if (snprintf(n, sizeof(n), "enum %s",
> + name == NULL ? "(anon)" : name) < 0)
> + return dt_ctf_set_errno(dtp, ECTF_NAMELEN);
> +
> + ctfid = ctf_lookup_by_name(ctf, n);
> + break;
> + case BTF_KIND_UNION:
> + if (snprintf(n, sizeof(n), "union %s",
> + name == NULL ? "(anon)" : name) < 0)
> + return dt_ctf_set_errno(dtp, ECTF_NAMELEN);
> +
> + ctfid = ctf_lookup_by_name(ctf, n);
> + break;
> + case BTF_KIND_STRUCT:
> + if (snprintf(n, sizeof(n), "struct %s",
> + name == NULL ? "(anon)" : name) < 0)
> + return dt_ctf_set_errno(dtp, ECTF_NAMELEN);
> +
> + ctfid = ctf_lookup_by_name(ctf, n);
> + break;
> + default:
> + ctfid = ctf_lookup_by_name(ctf, name);
> + }
> +
> + if (ctfid != CTF_ERR) {
> + btf->ctfids[type_id] = ctfid;
> + return ctfid;
> + }
> + }
> +
> + switch (kind) {
> + case BTF_KIND_INT: {
> + uint32_t data = *(uint32_t *)(type + 1);
> + uint32_t benc = BTF_INT_ENCODING(data);
> +
> + enc.cte_format = 0;
> + enc.cte_offset = BTF_INT_OFFSET(data);
> + enc.cte_bits = BTF_INT_BITS(data);
> +
> + if (benc & BTF_INT_SIGNED)
> + enc.cte_format |= CTF_INT_SIGNED;
> + if (benc & BTF_INT_CHAR)
> + enc.cte_format |= CTF_INT_CHAR;
> + if (benc & BTF_INT_BOOL)
> + enc.cte_format |= CTF_INT_BOOL;
> +
> + /* Some special case handling. */
> + if (enc.cte_bits == 8) {
> + if (strcmp(name, "_Bool") == 0)
> + enc.cte_format = CTF_INT_BOOL;
> + else
> + enc.cte_format = CTF_INT_CHAR;
> + }
> +
> + ctfid = ctf_add_integer(ctf, CTF_ADD_ROOT, name, &enc);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_FLOAT: {
> + switch (type->size) {
> + case 4:
> + enc.cte_format = CTF_FP_SINGLE;
> + break;
> + case 8:
> + enc.cte_format = CTF_FP_DOUBLE;
> + break;
> + case 16:
> + enc.cte_format = CTF_FP_LDOUBLE;
> + break;
> + }
> +
> + enc.cte_offset = 0;
> + enc.cte_bits = type->size * 8;
> +
> + ctfid = ctf_add_float(ctf, CTF_ADD_ROOT, name, &enc);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_ENUM: {
> + int i;
> + btf_enum_t *item = (btf_enum_t *)(type + 1);
> +
> + ctfid = ctf_add_enum(ctf, CTF_ADD_ROOT, name);
> + if (ctfid == CTF_ERR)
> + return dt_ctf_error(dtp, ctf);
> +
> + for (i = 0; i < vlen; i++, item++) {
> + int err;
> + const char *iname;
> +
> + iname = dt_btf_get_string(dtp, btf, item->name_off);
> + err = ctf_add_enumerator(ctf, ctfid, iname, item->val);
> + if (err == CTF_ERR)
> + return dt_ctf_error(dtp, ctf);
> + }
> +
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_PTR: {
> + ctfid = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctfid == CTF_ERR)
> + return CTF_ERR; /* errno already set */
> +
> + ctfid = ctf_add_pointer(ctf, CTF_ADD_ROOT, ctfid);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_CONST: {
> + ctfid = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctfid == CTF_ERR)
> + return CTF_ERR; /* errno already set */
> +
> + ctfid = ctf_add_const(ctf, CTF_ADD_ROOT, ctfid);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_VOLATILE: {
> + ctfid = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctfid == CTF_ERR)
> + return CTF_ERR; /* errno already set */
> +
> + ctfid = ctf_add_volatile(ctf, CTF_ADD_ROOT, ctfid);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_RESTRICT: {
> + ctfid = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctfid == CTF_ERR)
> + return CTF_ERR; /* errno already set */
> +
> + ctfid = ctf_add_restrict(ctf, CTF_ADD_ROOT, ctfid);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_ARRAY: {
> + btf_array_t *arr = (btf_array_t *)(type + 1);
> + ctf_arinfo_t arp;
> +
> + arp.ctr_contents = dt_btf_add_to_ctf(dtp, btf, ctf, arr->type);
> + if (arp.ctr_contents == CTF_ERR)
> + return CTF_ERR;
> + arp.ctr_index = dt_btf_add_to_ctf(dtp, btf, ctf, arr->index_type);
> + if (arp.ctr_contents == CTF_ERR)
> + return CTF_ERR;
> + arp.ctr_nelems = arr->nelems;
> +
> + ctfid = ctf_add_array(ctf, CTF_ADD_ROOT, &arp);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_TYPEDEF: {
> + ctfid = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctfid == CTF_ERR)
> + return CTF_ERR; /* errno already set */
> +
> + ctfid = ctf_add_typedef(ctf, CTF_ADD_ROOT, name, ctfid);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_STRUCT:
> + case BTF_KIND_UNION: {
> + int i;
> + btf_member_t *item = (btf_member_t *)(type + 1);
> +
> + if (kind == BTF_KIND_STRUCT)
> + ctfid = ctf_add_struct_sized(ctf, CTF_ADD_ROOT, name,
> + type->size);
> + else
> + ctfid = ctf_add_union_sized(ctf, CTF_ADD_ROOT, name,
> + type->size);
> +
> + btf->ctfids[type_id] = ctfid;
> + if (ctfid == CTF_ERR)
> + return dt_ctf_error(dtp, ctf);
> +
> + for (i = 0; i < vlen; i++, item++) {
> + int err;
> + ctf_id_t mbid;
> + const char *mname;
> +
> + mbid = dt_btf_add_to_ctf(dtp, btf, ctf, item->type);
> + if (mbid == CTF_ERR)
> + return CTF_ERR;
> + mname = dt_btf_get_string(dtp, btf, item->name_off);
> +
> + if (BTF_INFO_KFLAG(type->info)) {
> + enc.cte_format = 0;
> + enc.cte_offset = 0;
> + enc.cte_bits = BTF_MEMBER_BITFIELD_SIZE(item->offset);
> + item->offset = BTF_MEMBER_BIT_OFFSET(item->offset);
> +
> + if (enc.cte_bits > 0) {
> + int kind;
> +
> + /* Resolve member type for CTF. */
> + mbid = ctf_type_resolve(ctf, mbid);
> + kind = ctf_type_kind(ctf, mbid);
> +
> + if (kind != CTF_K_INTEGER &&
> + kind != CTF_K_FLOAT &&
> + kind != CTF_K_ENUM)
> + return dt_ctf_set_errno(
> + dtp, ECTF_NOTINTFP);
> +
> + mbid = ctf_add_slice(ctf,
> + CTF_ADD_NONROOT,
> + mbid, &enc);
> + if (mbid == CTF_ERR)
> + return dt_ctf_error(dtp, ctf);
> +
> + /* Fall-through */
> + }
> +
> + /* Fall-through - add as regular member. */
> + }
> +
> + err = ctf_add_member_offset(ctf, ctfid, mname, mbid,
> + item->offset);
> + if (err == CTF_ERR)
> + return dt_ctf_error(dtp, ctf);
> + }
> +
> + return ctfid;
> + }
> + case BTF_KIND_FUNC_PROTO: {
> + int i;
> + ctf_id_t argv[vlen];
> + btf_param_t *item = (btf_param_t *)(type + 1);
> + ctf_funcinfo_t ctc;
> +
> + ctc.ctc_flags = 0;
> + ctc.ctc_argc = vlen;
> + ctc.ctc_return = dt_btf_add_to_ctf(dtp, btf, ctf, type->type);
> + if (ctc.ctc_return == CTF_ERR)
> + return CTF_ERR;
> +
> + for (i = 0; i < vlen; i++) {
> + argv[i] = dt_btf_add_to_ctf(dtp, btf, ctf, item->type);
> + if (argv[i] == CTF_ERR)
> + return CTF_ERR;
> + }
> +
> + ctfid = ctf_add_function(ctf, CTF_ADD_ROOT, &ctc, argv);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_FWD: {
> + uint32_t kind;
> +
> + kind = BTF_INFO_KFLAG(type->info) ? CTF_K_UNION : CTF_K_STRUCT;
> + ctfid = ctf_add_forward(ctf, CTF_ADD_ROOT, name, kind);
> + btf->ctfids[type_id] = ctfid;
> +
> + return ctfid == CTF_ERR ? dt_ctf_error(dtp, ctf) : ctfid;
> + }
> + case BTF_KIND_VAR:
> + case BTF_KIND_DATASEC:
> + case BTF_KIND_DECL_TAG:
> + case BTF_KIND_TYPE_TAG:
> + case BTF_KIND_FUNC:
> + case BTF_KIND_ENUM64:
> + return btf->ctfids[0]; /* Ignored for CTF */
> + default:
> + return dt_ctf_error(dtp, ctf);
> + }
> +
> + assert(0);
> +}
> +
> +ctf_dict_t *
> +dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
> +{
> + int i, base = 0;
> + ctf_dict_t *ctf;
> + ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 0 };
> +
> + ctf = ctf_create(&dtp->dt_ctferr);
> + if (ctf == NULL)
> + return NULL;
> +
> + ctf_setmodel(ctf, dtp->dt_conf.dtc_ctfmodel);
> +
> + /*
> + * All modules import 'shared_ctf'. The 'vmlinux' module is handled as
> + * a special case because it simply imports 'shared_ctf'.
> + */
> + if (dmp) {
> + int err;
> +
> + err = ctf_import(ctf, dtp->dt_shared_ctf);
> + if (err == CTF_ERR)
> + return NULL;
> +
> + ctf_setspecific(ctf, dmp);
> +
> + if (strcmp(dmp->dm_name, "vmlinux") == 0)
> + return ctf;
> + }
> +
> + 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;
> +
> + /*
> + * If we are constructing 'shared_ctf' (no module), we need to add an
> + * entry for the implicit 'void' BTF type (id 0).
> + */
> + if (!dmp) {
> + btf->ctfids[0] = ctf_add_integer(ctf, CTF_ADD_ROOT, "void",
> + &enc);
> + if (btf->ctfids[0] == CTF_ERR)
> + dt_dprintf("Could not create 'void' CTF entry.\n");
> + }
> +
> + /*
> + * 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;
> +
> + if (dt_btf_add_to_ctf(dtp, btf, ctf, type_id) == CTF_ERR) {
> + dt_dprintf("BTF-to-CTF error: %s\n",
> + ctf_errmsg(dtp->dt_ctferr));
> + break;
> + }
> + }
> +
> + if (ctf_update(ctf) == CTF_ERR)
> + return NULL;
> +
> + return ctf;
> +}
> +
> +ctf_dict_t *
> +dt_btf_load_module(dtrace_hdl_t *dtp, dt_module_t *dmp)
> +{
> + char fn[PATH_MAX + 1];
> + dt_btf_t *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;
> +
> + dt_dprintf("Generated shared CTF from BTF (%d types).\n",
> + btf->type_cnt);
> +
> + return dtp->dt_shared_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);
> +}
> +
> +const char *
> +dt_btf_get_string(dtrace_hdl_t *dtp, dt_btf_t *btf, uint32_t off)
> +{
> + if (dtp->dt_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;
> +
> + off -= dtp->dt_btf->hdr->str_len;
> +ok:
> + if (off < btf->hdr->str_len)
> + return btf->sdata + off;
> +
> + return dt_btf_set_load_errno(dtp, EINVAL);
> +}
> +
> +int32_t
> +dt_btf_lookup_name_kind(dtrace_hdl_t *dtp, dt_btf_t *btf, const char *name,
> + uint32_t kind)
> +{
> + int32_t i;
> +
> + if (kind == BTF_KIND_UNKN)
> + return 0;
> + if (strcmp(name, "void") == 0)
> + return 0;
> +
> + for (i = 1; i < btf->type_cnt; i++) {
> + const btf_type_t *type = btf->types[i];
> + const char *str;
> +
> + if (BTF_INFO_KIND(type->info) != kind)
> + continue;
> +
> + str = dt_btf_get_string(dtp, btf, type->name_off);
> + if (strcmp(name, str) == 0)
> + return i;
> + }
> +
> + return -ENOENT;
> +}
> diff --git a/libdtrace/dt_btf.h b/libdtrace/dt_btf.h
> new file mode 100644
> index 00000000..3db4203a
> --- /dev/null
> +++ b/libdtrace/dt_btf.h
> @@ -0,0 +1,27 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +#ifndef _DT_BTF_H
> +#define _DT_BTF_H
> +
> +#include <ctf.h>
> +#include <dt_impl.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +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_get_string(dtrace_hdl_t *, dt_btf_t *, uint32_t);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _DT_BTF_H */
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index f6025817..48b34ec9 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -328,9 +328,10 @@ struct dtrace_hdl {
> dt_list_t dt_modlist; /* linked list of dt_module_t's */
> dt_htab_t *dt_mods; /* hash table of dt_module_t's */
> 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 */
> ctf_file_t *dt_shared_ctf; /* Handle to the shared CTF */
> - char *dt_ctfa_path; /* path to vmlinux.ctfa */
> 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 */
> @@ -417,6 +418,7 @@ struct dtrace_hdl {
> uint64_t dt_options[DTRACEOPT_MAX]; /* dtrace run-time options */
> uint32_t dt_bpfhelper[__BPF_FUNC_MAX_ID]; /* BPF helper mapping */
> int dt_version; /* library version requested by client */
> + int dt_btferr; /* error resulting from last BTF failure */
> int dt_ctferr; /* error resulting from last CTF failure */
> int dt_errno; /* error resulting from last failed operation */
> int dt_cdefs_fd; /* file descriptor for C CTF debugging cache */
> diff --git a/libdtrace/dt_module.c b/libdtrace/dt_module.c
> index 28fa176c..bb8c030d 100644
> --- a/libdtrace/dt_module.c
> +++ b/libdtrace/dt_module.c
> @@ -26,6 +26,7 @@
> #include <dt_kernel_module.h>
> #include <dt_module.h>
> #include <dt_impl.h>
> +#include <dt_btf.h>
> #include <dt_string.h>
>
> #define KSYM_NAME_MAX 128 /* from kernel/scripts/kallsyms.c */
> @@ -806,6 +807,7 @@ sym_type_to_info(char info)
>
> return GELF_ST_INFO(binding, type);
> }
> +
> /*
> * Do all necessary post-creation initialization of a module of type
> * DT_DM_KERNEL.
> @@ -825,6 +827,28 @@ dt_kern_module_init(dtrace_hdl_t *dtp, dt_module_t *dmp)
> return 0;
> }
>
> +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);
> +
> + dt_dprintf("Generating CTF for module %s from BTF.\n", dmp->dm_name);
> +
> + dmp->dm_ctfp = dt_btf_load_module(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",
> + dmp->dm_name, ctf_errmsg(dtp->dt_ctferr));
> + else
> + dmp->dm_flags |= DT_DM_CTF_ARCHIVED;
> +}
> +
> /*
> * Determine the location of a kernel module's CTF data.
> *
> @@ -843,6 +867,16 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
> dt_kern_module_init(dtp, dmp);
> }
>
> + /*
> + * 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;
> + }
> +
> /*
> * 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
> @@ -868,17 +902,10 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
> strlen("/kernel/vmlinux.ctfa") + 1);
> to = stpcpy(ctfa_name, dtp->dt_module_path);
> stpcpy(to, "/kernel/vmlinux.ctfa");
> - } else {
> + } else
> ctfa_name = dtp->dt_ctfa_path;
> - }
> -
> - if ((dtp->dt_ctfa = ctf_arc_open(ctfa_name,
> - &dtp->dt_ctferr)) == NULL) {
> - dt_dprintf("Cannot open CTF archive %s: %s; looking "
> - "for in-module CTF instead.\n", ctfa_name,
> - ctf_errmsg(dtp->dt_ctferr));
> - }
>
> + dtp->dt_ctfa = ctf_arc_open(ctfa_name, &dtp->dt_ctferr);
> if (dtp->dt_ctfa != NULL) {
> /*
> * Load in the shared CTF immediately.
> @@ -895,6 +922,18 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
> dt_dprintf("Loaded shared CTF from archive %s.\n",
> ctfa_name);
> }
> + } else {
> + dt_dprintf("Cannot open CTF archive %s: %s; "
> + "trying BTF.\n",
> + ctfa_name, ctf_errmsg(dtp->dt_ctferr));
> +
> + /*
> + * 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"));
> + return;
> }
>
> if (dtp->dt_ctfa_path == NULL)
More information about the DTrace-devel
mailing list