From: Thierry Treyer via B4 Relay <devnull+ttreyer.meta.com@kernel.org>
To: dwarves@vger.kernel.org, bpf@vger.kernel.org
Cc: Thierry Treyer <ttreyer@meta.com>,
acme@kernel.org, ast@kernel.org, yhs@meta.com,
andrii@kernel.org, ihor.solodrai@linux.dev,
songliubraving@meta.com, alan.maguire@oracle.com,
mykolal@meta.com
Subject: [PATCH RFC 3/3] inline_encoder: Introduce inline encoder to emit BTF.inline
Date: Wed, 16 Apr 2025 19:20:37 +0000 [thread overview]
Message-ID: <20250416-btf_inline-v1-3-e4bd2f8adae5@meta.com> (raw)
In-Reply-To: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com>
From: Thierry Treyer <ttreyer@meta.com>
Signed-off-by: Thierry Treyer <ttreyer@meta.com>
---
CMakeLists.txt | 3 +-
btf_encoder.c | 5 +
btf_encoder.h | 2 +
btf_inline.pk | 55 ++++++
inline_encoder.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
inline_encoder.h | 25 +++
pahole.c | 40 ++++-
7 files changed, 623 insertions(+), 3 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ca17a6789a9e71512ffb6fe5bf2683d643209e5..38302d5b6a6ded5094ebecd83fec9eca16230be2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,7 +109,8 @@ endif()
set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer.c
ctf_loader.c libctf.c btf_encoder.c btf_loader.c
- dwarf_loader.c dutil.c elf_symtab.c rbtree.c)
+ inline_encoder.c dwarf_loader.c dutil.c
+ elf_symtab.c rbtree.c)
if (NOT LIBBPF_FOUND)
list(APPEND dwarves_LIB_SRCS $<TARGET_OBJECTS:bpf>)
endif()
diff --git a/btf_encoder.c b/btf_encoder.c
index 511c1ea5ee6bee67dbaec89c3d8e415af5c8e674..ead62e9034140c257e7703f58270aba19f422582 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -2682,3 +2682,8 @@ out:
encoder->cu = NULL;
return err;
}
+
+struct btf *btf_encoder__btf(struct btf_encoder *encoder)
+{
+ return encoder->btf;
+}
diff --git a/btf_encoder.h b/btf_encoder.h
index 0f345e20c2d3065511d46d7377403a4a5fbdfdef..42d21b1cbfe71fd6ee54f41a8ae153197b3021c6 100644
--- a/btf_encoder.h
+++ b/btf_encoder.h
@@ -29,4 +29,6 @@ void btf_encoder__delete(struct btf_encoder *encoder);
int btf_encoder__encode(struct btf_encoder *encoder, struct conf_load *conf);
int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct conf_load *conf_load);
+struct btf *btf_encoder__btf(struct btf_encoder *encoder);
+
#endif /* _BTF_ENCODER_H_ */
diff --git a/btf_inline.pk b/btf_inline.pk
new file mode 100755
index 0000000000000000000000000000000000000000..b7b984f189c9d0e6f82dc993005164e0d3609333
--- /dev/null
+++ b/btf_inline.pk
@@ -0,0 +1,55 @@
+#!/usr/bin/poke -L
+!#
+
+type II_location_op = struct {
+ uint<8> ty;
+ offset<uint<8>, B> size;
+ union {
+ byte[0] nil: ty == 0;
+ int<8> signed_const_1: ty == 1;
+ int<16> signed_const_2: ty == 2;
+ int<32> signed_const_4: ty == 3;
+ int<64> signed_const_8: ty == 4;
+ uint<8> unsigned_const_1: ty == 5;
+ uint<16> unsigned_const_2: ty == 6;
+ uint<32> unsigned_const_4: ty == 7;
+ uint<64> unsigned_const_8: ty == 8;
+ struct {
+ uint<8> register;
+ uint<64> offset;
+ } register: ty == 9;
+ } value;
+};
+
+type II_inline_instance = struct {
+ uint<64> insn_offset;
+ uint<32> type_id;
+ uint<16> param_count;
+ offset<uint<32>, B>[param_count] param_offsets;
+};
+
+type II_Header = struct {
+ uint<16> magic == 0xeb9f;
+ uint<8> version;
+ uint<8> flags;
+ uint<32> header_size;
+ offset<uint<32>, B> inline_info_offset;
+ offset<uint<32>, B> inline_info_size;
+ offset<uint<32>, B> location_offset;
+ offset<uint<32>, B> location_size;
+};
+
+type II = struct {
+ II_Header hdr;
+ II_inline_instance[hdr.inline_info_size] inline_instances @ hdr.inline_info_offset;
+ II_location_op[hdr.location_size] locations @ hdr.location_offset;
+};
+
+if (!poke_interactive_p) {
+ var inline_file = open("/tmp/inline_expansions.btf", IOS_M_RDONLY);
+ var inline_info = II @ inline_file : 0#B;
+
+ for (exp in inline_info.inline_instances) {
+ printf ("{low_pc => '0x%u64x',type_id => %u32d}\n", exp.insn_offset, exp.type_id);
+ }
+}
diff --git a/inline_encoder.c b/inline_encoder.c
new file mode 100644
index 0000000000000000000000000000000000000000..b4a681bd996088109ddf0a8167690ce83026324b
--- /dev/null
+++ b/inline_encoder.c
@@ -0,0 +1,496 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2025 Facebook
+
+ Derived from ctf_encoder.c, which is:
+
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ Copyright (C) Red Hat Inc
+ */
+#include <bpf/btf.h>
+
+#include "dutil.h"
+#include "dwarves.h"
+#include "inline_encoder.h"
+#include "list.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+enum loc_type {
+ LOC_END_OF_EXPR,
+ LOC_SIGNED_CONST_1,
+ LOC_SIGNED_CONST_2,
+ LOC_SIGNED_CONST_4,
+ LOC_SIGNED_CONST_8,
+ LOC_UNSIGNED_CONST_1,
+ LOC_UNSIGNED_CONST_2,
+ LOC_UNSIGNED_CONST_4,
+ LOC_UNSIGNED_CONST_8,
+ LOC_REGISTER,
+} __attribute__((packed));
+
+struct loc {
+ enum loc_type type;
+ uint8_t size;
+};
+
+struct inline_parameter {
+ struct loc *location[16];
+};
+
+struct inline_instance {
+ struct list_head node;
+ uint64_t die_offset;
+ const char *name;
+ uint64_t insn_offset;
+ type_id_t type_id;
+ uint16_t param_count;
+ struct inline_parameter parameters[];
+};
+
+struct inline_encoder {
+ struct btf *btf;
+ struct cu *cu;
+ const char *source_filename;
+ const char *filename;
+ size_t inline_instance_cnt;
+
+ struct list_head inline_instances;
+};
+
+struct loc_node {
+ struct rb_node rb_node;
+ struct list_head node;
+ struct loc loc;
+};
+
+struct inline_encoder__header {
+ uint16_t magic;
+ uint8_t version;
+ uint8_t flags;
+ uint32_t header_size;
+ uint32_t inline_info_offset;
+ uint32_t inline_info_size;
+ uint32_t location_offset;
+ uint32_t location_size;
+};
+
+struct inline_encoder *inline_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load)
+{
+ struct inline_encoder *encoder = zalloc(sizeof(*encoder));
+
+ if (encoder) {
+ encoder->cu = cu;
+ encoder->source_filename = strdup(cu->filename);
+ encoder->filename = strdup(detached_filename ?: cu->filename);
+ encoder->btf = base_btf;
+ encoder->inline_instance_cnt = 0;
+
+ INIT_LIST_HEAD(&encoder->inline_instances);
+ }
+
+ return encoder;
+}
+
+void inline_encoder__delete(struct inline_encoder *encoder)
+{
+ if (encoder == NULL)
+ return;
+
+ zfree(&encoder->source_filename);
+ zfree(&encoder->filename);
+ encoder->btf = NULL; // Non-owning pointer to base BTF
+
+ struct inline_instance *exp, *n;
+ list_for_each_entry_safe_reverse(exp, n, &encoder->inline_instances, node) {
+ list_del_init(&exp->node);
+ for (size_t i = 0; i < exp->param_count; ++i)
+ for (size_t j = 0; j < 16; ++j)
+ free(exp->parameters[i].location[j]);
+ free(exp);
+ }
+
+ free(encoder);
+}
+
+static size_t inline_instance__sizeof(uint16_t param_count)
+{
+ return offsetof(struct inline_instance, parameters) + param_count * sizeof(struct inline_parameter);
+}
+
+static struct loc *loc__make_const(bool is_signed, size_t size, uint64_t value)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc) + size);
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->size = sizeof(struct loc) + size;
+ switch (size) {
+ case 1:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_1 : LOC_UNSIGNED_CONST_1;
+ break;
+ case 2:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_2 : LOC_UNSIGNED_CONST_2;
+ break;
+ case 4:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_4 : LOC_UNSIGNED_CONST_4;
+ break;
+ case 8:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_8 : LOC_UNSIGNED_CONST_8;
+ break;
+ default:
+ free(new_loc);
+ return NULL;
+ }
+ uint8_t *data = (uint8_t *)new_loc + sizeof(struct loc);
+ memcpy(data, &value, size);
+ return new_loc;
+}
+
+static struct loc *loc__make_register(uint8_t reg, int64_t offset)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc) + sizeof(reg) + sizeof(offset));
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->size = sizeof(struct loc) + sizeof(uint8_t) + sizeof(int64_t);
+ new_loc->type = LOC_REGISTER;
+ uint8_t *data = (uint8_t *)new_loc + sizeof(struct loc);
+ *data = reg;
+ data += sizeof(uint8_t);
+ memcpy(data, &offset, sizeof(int64_t));
+ return new_loc;
+}
+
+static struct loc *loc__make_eoe(void)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc));
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->type = LOC_END_OF_EXPR;
+ new_loc->size = sizeof(*new_loc);
+ return new_loc;
+}
+
+static uint32_t expr__size(struct loc *expr[16])
+{
+ uint32_t size = 0;
+ for (size_t i = 0; i < 16; ++i) {
+ if (expr[i] == NULL)
+ break;
+ size += expr[i]->size;
+ }
+ return size;
+}
+
+static int inline_encoder__encode_location(struct inline_encoder *encoder, struct location *loc, struct loc *expr[16])
+{
+ if (loc->expr == NULL && loc->exprlen == 0)
+ return 0;
+
+ if (loc->expr == NULL) {
+ expr[0] = loc__make_const(false, 8, loc->exprlen);
+ return 0;
+ }
+
+ size_t expr_i = 0;
+ for (size_t i = 0; i < loc->exprlen; ++i) {
+ Dwarf_Op op = loc->expr[i];
+ switch (op.atom) {
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 1, op.number);
+ break;
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 2, op.number);
+ break;
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 4, op.number);
+ break;
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 8, op.number);
+ break;
+ case DW_OP_constu:
+ case DW_OP_consts:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_consts, 8, op.number);
+ break;
+ case DW_OP_lit0 ... DW_OP_lit31:
+ expr[expr_i++] = loc__make_const(false, 1, op.atom - DW_OP_lit0);
+ break;
+ case DW_OP_reg0 ... DW_OP_reg31:
+ expr[expr_i++] = loc__make_register(op.atom - DW_OP_reg0, 0);
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31: {
+ expr[expr_i++] = loc__make_register(op.atom - DW_OP_breg0, op.number);
+ break;
+ }
+ case DW_OP_stack_value: {
+ // no-op
+ break;
+ }
+ default:
+ goto out_err;
+ }
+ if (expr_i >= 16)
+ goto out_err;
+ }
+
+ expr[expr_i++] = loc__make_eoe();
+ return 0;
+
+out_err:
+ for (size_t i = 0; i < expr_i; ++i) {
+ free(expr[i]);
+ expr[i] = NULL;
+ }
+
+ return -1;
+}
+
+static inline struct dwarf_tag *tag__dwarf(const struct tag *tag)
+{
+ uint8_t *data = (uint8_t *)tag;
+ return (struct dwarf_tag *)data;
+}
+
+static inline uint64_t die_offset(const struct tag *tag)
+{
+ uint8_t *data = (uint8_t *)tag;
+ data -= 64;
+ data += 24;
+ return *(uint64_t *)data;
+}
+
+static int inline_encoder__save_inline_expansion(struct inline_encoder *encoder, struct inline_expansion *exp)
+{
+ if (exp->name == NULL)
+ return 0;
+
+ struct inline_instance *instance = zalloc(inline_instance__sizeof(exp->nr_parms));
+ if (instance == NULL)
+ return -ENOMEM;
+
+ instance->die_offset = die_offset((struct tag *)exp);
+ instance->name = exp->name;
+ instance->insn_offset = exp->ip.addr;
+ instance->type_id = -1;
+ instance->param_count = exp->nr_parms;
+ // printf("inline instance for %u (%s): %lx %lx\n", instance->type_id, exp->name, instance->die_offset, instance->insn_offset);
+
+ uint32_t param_index = 0;
+ struct parameter *param = NULL;
+ list_for_each_entry(param, &exp->parms, tag.node) {
+ inline_encoder__encode_location(encoder, ¶m->location, instance->parameters[param_index++].location);
+ }
+
+ INIT_LIST_HEAD(&instance->node);
+ list_add_tail(&instance->node, &encoder->inline_instances);
+ encoder->inline_instance_cnt++;
+
+ return 0;
+}
+
+static int inline_encoder__encode_lexblock(struct inline_encoder *encoder, struct lexblock *lexblock, struct conf_load *conf_load)
+{
+ int err = 0;
+
+ struct tag *tag = NULL;
+ list_for_each_entry(tag, &lexblock->tags, node) {
+ if (tag->tag == DW_TAG_lexical_block) {
+ err = inline_encoder__encode_lexblock(encoder, tag__lexblock(tag), conf_load);
+ if (err)
+ goto out;
+ continue;
+ } else if (tag->tag != DW_TAG_inlined_subroutine) {
+ continue;
+ }
+
+ struct inline_expansion *exp = tag__inline_expansion(tag);
+ err = inline_encoder__save_inline_expansion(encoder, exp);
+ if (err)
+ goto out;
+ }
+
+ return err;
+
+out:
+ return err;
+}
+
+int inline_encoder__encode_cu(struct inline_encoder *encoder, struct cu *cu, struct conf_load *conf_load)
+{
+ int err = 0;
+
+ uint32_t fn_id;
+ struct function *fn;
+ cu__for_each_function(cu, fn_id, fn) {
+ if (fn->declaration)
+ continue;
+ if (function__addr(fn) == 0)
+ continue;
+
+ err = inline_encoder__encode_lexblock(encoder, &fn->lexblock, conf_load);
+ if (err)
+ goto out;
+ }
+ return err;
+out:
+ return err;
+}
+
+struct node_type_id {
+ const char *name;
+ type_id_t type_id;
+};
+
+static int cmpstrp(const void *left, const void *right)
+{
+ struct node_type_id *left_node = (struct node_type_id*)left;
+ struct node_type_id *right_node = (struct node_type_id*)right;
+ return strcmp(left_node->name, right_node->name);
+}
+
+static struct node_type_id *inline_encoder__build_type_id_cache(struct inline_encoder *encoder)
+{
+ struct node_type_id *exps = (struct node_type_id*)malloc(encoder->inline_instance_cnt * sizeof(struct node_type_id));
+
+ size_t exp_id = 0;
+ struct inline_instance *exp;
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ exps[exp_id++] = (struct node_type_id){exp->name, 0};
+ }
+
+ qsort(exps, encoder->inline_instance_cnt, sizeof(struct node_type_id), cmpstrp);
+
+ return exps;
+}
+
+static int inline_encoder__write_raw_file(struct inline_encoder *encoder, const char *filename)
+{
+ int err = -1;
+
+ int fd = creat(filename, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ fprintf(stderr, "%s open(%s) failed!\n", __func__, filename);
+ goto out;
+ }
+
+ struct inline_encoder__header header = {
+ .magic = 0xeb9f,
+ .version = 1,
+ .flags = 0,
+ .header_size = sizeof(header),
+ .inline_info_offset = sizeof(struct inline_encoder__header),
+ .inline_info_size = 0,
+ .location_offset = 0,
+ .location_size = 2,
+ };
+ write(fd, &header, header.header_size);
+
+ struct node_type_id *type_id_cache = inline_encoder__build_type_id_cache(encoder);
+
+ struct inline_instance *exp;
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ struct node_type_id lookup_key = { exp->name, 0 };
+ struct node_type_id *found = bsearch(&lookup_key, type_id_cache, encoder->inline_instance_cnt, sizeof(lookup_key), cmpstrp);
+ assert(found != NULL);
+ if (found->type_id == 0) {
+ int type_id = btf__find_by_name_kind(encoder->btf, exp->name, BTF_KIND_FUNC);
+ found->type_id = (type_id < 0) ? -1 : type_id;
+ }
+ exp->type_id = found->type_id;
+ if (exp->type_id == -1) continue;
+
+ const void *data = exp;
+ header.inline_info_size += write(
+ fd,
+ data + offsetof(struct inline_instance, insn_offset),
+ inline_instance__sizeof(0)
+ - offsetof(struct inline_instance, insn_offset)
+ - 2); // Skip padding at the end of the struct
+ for (uint16_t i = 0; i < exp->param_count; ++i) {
+ struct inline_parameter *param = &exp->parameters[i];
+ if (param->location[0] == NULL) {
+ uint32_t zero = 0;
+ header.inline_info_size += write(fd, &zero, sizeof(zero));
+ } else {
+ header.inline_info_size += write(fd, &header.location_size, sizeof(header.location_size));
+ header.location_size += expr__size(param->location);
+ }
+ }
+ }
+
+ free(type_id_cache);
+
+ struct loc end_of_expr = {
+ .type = LOC_END_OF_EXPR,
+ .size = sizeof(end_of_expr),
+ };
+ write(fd, &end_of_expr, sizeof(end_of_expr));
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ if (exp->type_id == -1)
+ continue;
+ for (uint16_t i = 0; i < exp->param_count; ++i) {
+ struct inline_parameter *param = &exp->parameters[i];
+ for (size_t j = 0; j < 16; ++j) {
+ struct loc *op = param->location[j];
+ if (op == NULL)
+ break;
+ write(fd, op, op->size);
+ }
+ }
+ }
+ header.location_offset = header.inline_info_offset + header.inline_info_size;
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &header, header.header_size);
+
+ close(fd);
+ return 0;
+
+out:
+ if (fd != - 1)
+ close(fd);
+ unlink(filename);
+ return err;
+}
+
+int inline_encoder__encode(struct inline_encoder *encoder, struct conf_load *conf_load)
+{
+ char tmp_fn[PATH_MAX];
+ snprintf(tmp_fn, sizeof(tmp_fn), "%s.btf_inline", encoder->filename);
+
+ int err = inline_encoder__write_raw_file(encoder, tmp_fn);
+ if (err) return err;
+
+ const char *llvm_objcopy = getenv("LLVM_OBJCOPY");
+ if (!llvm_objcopy)
+ llvm_objcopy = "llvm-objcopy";
+
+ char cmd[PATH_MAX * 2];
+ snprintf(cmd, sizeof(cmd), "%s --add-section .BTF_inline=%s %s",
+ llvm_objcopy, tmp_fn, encoder->filename);
+ if (system(cmd)) {
+ fprintf(stderr, "%s: failed to add .BTF_inline section '%s': %d!\n",
+ __func__, tmp_fn, errno);
+ err = -1;
+ goto unlink;
+ }
+
+ err = 0;
+unlink:
+ unlink(tmp_fn);
+ return err;
+}
+
+void inline_encoder__set_btf(struct inline_encoder *encoder, struct btf *btf)
+{
+ encoder->btf = btf;
+}
diff --git a/inline_encoder.h b/inline_encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3472d011a56e84efc77d85fcee1a6aa88204c86
--- /dev/null
+++ b/inline_encoder.h
@@ -0,0 +1,25 @@
+#ifndef _INLINE_ENCODER_H_
+#define _INLINE_ENCODER_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2025 Facebook
+
+ Derived from btf_encoder.h, which is:
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include <stdbool.h>
+
+struct inline_encoder;
+struct conf_load;
+struct btf;
+struct cu;
+
+struct inline_encoder *inline_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load);
+void inline_encoder__delete(struct inline_encoder *encoder);
+int inline_encoder__encode_cu(struct inline_encoder *encoder, struct cu *cu, struct conf_load *conf_load);
+int inline_encoder__encode(struct inline_encoder *encoder, struct conf_load *conf_load);
+
+void inline_encoder__set_btf(struct inline_encoder *encoder, struct btf *btf);
+
+#endif /* _INLINE_ENCODER_H_ */
diff --git a/pahole.c b/pahole.c
index af3e1cfe224dd14a5be2c072461505cf3cc387c5..093d76478dbd5a687c44b0f19fce73cfe6aae161 100644
--- a/pahole.c
+++ b/pahole.c
@@ -28,12 +28,15 @@
#include "dutil.h"
//#include "ctf_encoder.h" FIXME: disabled, probably its better to move to Oracle's libctf
#include "btf_encoder.h"
+#include "inline_encoder.h"
static struct btf_encoder *btf_encoder;
+static struct inline_encoder *inline_encoder;
static char *detached_btf_filename;
struct cus *cus;
static bool btf_encode;
static bool ctf_encode;
+static bool inline_encode;
static bool sort_output;
static bool need_resort;
static bool first_obj_only;
@@ -1638,6 +1641,11 @@ static const struct argp_option pahole__options[] = {
.key = 'J',
.doc = "Encode as BTF",
},
+ {
+ .name = "inline_encode",
+ .key = 'L',
+ .doc = "Encode inline expansions into .BTF.inline section",
+ },
{
.name = "btf_encode_detached",
.key = ARGP_btf_encode_detached,
@@ -1832,6 +1840,7 @@ static error_t pahole__options_parser(int key, char *arg,
break;
case ARGP_btf_encode_detached:
detached_btf_filename = arg; // fallthru
+ case 'L': inline_encode = inline_encode || key == 'L'; // fallthru
case 'J': btf_encode = 1;
conf_load.get_addr_info = true;
conf_load.ignore_alignment_attr = true;
@@ -3140,6 +3149,22 @@ static enum load_steal_kind pahole_stealer__btf_encode(struct cu *cu, struct con
return LSK__STOP_LOADING;
}
+ if (inline_encode) {
+ if (!inline_encoder)
+ inline_encoder = inline_encoder__new(cu, detached_btf_filename, conf_load->base_btf, global_verbose, conf_load);
+
+ if (!inline_encoder) {
+ fprintf(stderr, "Error creating inline encoder.\n");
+ return LSK__STOP_LOADING;
+ }
+
+ err = inline_encoder__encode_cu(inline_encoder, cu, conf_load);
+ if (err < 0) {
+ fprintf(stderr, "Error while encoding BTF.inline.\n");
+ return LSK__STOP_LOADING;
+ }
+ }
+
return LSK__DELETE;
}
@@ -3672,10 +3697,19 @@ try_sole_arg_as_class_names:
if (btf_encode && btf_encoder) { // maybe all CUs were filtered out and thus we don't have an encoder?
err = btf_encoder__encode(btf_encoder, &conf_load);
- btf_encoder__delete(btf_encoder);
if (err) {
fputs("Failed to encode BTF\n", stderr);
- goto out_cus_delete;
+ goto out_btf_encoder_delete;
+ }
+
+ if (inline_encode && inline_encoder) {
+ inline_encoder__set_btf(inline_encoder, btf_encoder__btf(btf_encoder));
+ err = inline_encoder__encode(inline_encoder, &conf_load);
+ inline_encoder__delete(inline_encoder);
+ if (err) {
+ fputs("Failed to encode BTF.inline\n", stderr);
+ goto out_btf_encoder_delete;
+ }
}
}
out_ok:
@@ -3683,6 +3717,8 @@ out_ok:
print_stats();
rc = EXIT_SUCCESS;
+out_btf_encoder_delete:
+ btf_encoder__delete(btf_encoder);
out_cus_delete:
#ifdef DEBUG_CHECK_LEAKS
cus__delete(cus);
--
2.47.1
WARNING: multiple messages have this Message-ID (diff)
From: Thierry Treyer <ttreyer@meta.com>
To: dwarves@vger.kernel.org, bpf@vger.kernel.org
Cc: Thierry Treyer <ttreyer@meta.com>,
acme@kernel.org, ast@kernel.org, yhs@meta.com,
andrii@kernel.org, ihor.solodrai@linux.dev,
songliubraving@meta.com, alan.maguire@oracle.com,
mykolal@meta.com
Subject: [PATCH RFC 3/3] inline_encoder: Introduce inline encoder to emit BTF.inline
Date: Wed, 16 Apr 2025 19:20:37 +0000 [thread overview]
Message-ID: <20250416-btf_inline-v1-3-e4bd2f8adae5@meta.com> (raw)
In-Reply-To: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com>
Signed-off-by: Thierry Treyer <ttreyer@meta.com>
---
CMakeLists.txt | 3 +-
btf_encoder.c | 5 +
btf_encoder.h | 2 +
btf_inline.pk | 55 ++++++
inline_encoder.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
inline_encoder.h | 25 +++
pahole.c | 40 ++++-
7 files changed, 623 insertions(+), 3 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ca17a6789a9e71512ffb6fe5bf2683d643209e5..38302d5b6a6ded5094ebecd83fec9eca16230be2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,7 +109,8 @@ endif()
set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer.c
ctf_loader.c libctf.c btf_encoder.c btf_loader.c
- dwarf_loader.c dutil.c elf_symtab.c rbtree.c)
+ inline_encoder.c dwarf_loader.c dutil.c
+ elf_symtab.c rbtree.c)
if (NOT LIBBPF_FOUND)
list(APPEND dwarves_LIB_SRCS $<TARGET_OBJECTS:bpf>)
endif()
diff --git a/btf_encoder.c b/btf_encoder.c
index 511c1ea5ee6bee67dbaec89c3d8e415af5c8e674..ead62e9034140c257e7703f58270aba19f422582 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -2682,3 +2682,8 @@ out:
encoder->cu = NULL;
return err;
}
+
+struct btf *btf_encoder__btf(struct btf_encoder *encoder)
+{
+ return encoder->btf;
+}
diff --git a/btf_encoder.h b/btf_encoder.h
index 0f345e20c2d3065511d46d7377403a4a5fbdfdef..42d21b1cbfe71fd6ee54f41a8ae153197b3021c6 100644
--- a/btf_encoder.h
+++ b/btf_encoder.h
@@ -29,4 +29,6 @@ void btf_encoder__delete(struct btf_encoder *encoder);
int btf_encoder__encode(struct btf_encoder *encoder, struct conf_load *conf);
int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct conf_load *conf_load);
+struct btf *btf_encoder__btf(struct btf_encoder *encoder);
+
#endif /* _BTF_ENCODER_H_ */
diff --git a/btf_inline.pk b/btf_inline.pk
new file mode 100755
index 0000000000000000000000000000000000000000..b7b984f189c9d0e6f82dc993005164e0d3609333
--- /dev/null
+++ b/btf_inline.pk
@@ -0,0 +1,55 @@
+#!/usr/bin/poke -L
+!#
+
+type II_location_op = struct {
+ uint<8> ty;
+ offset<uint<8>, B> size;
+ union {
+ byte[0] nil: ty == 0;
+ int<8> signed_const_1: ty == 1;
+ int<16> signed_const_2: ty == 2;
+ int<32> signed_const_4: ty == 3;
+ int<64> signed_const_8: ty == 4;
+ uint<8> unsigned_const_1: ty == 5;
+ uint<16> unsigned_const_2: ty == 6;
+ uint<32> unsigned_const_4: ty == 7;
+ uint<64> unsigned_const_8: ty == 8;
+ struct {
+ uint<8> register;
+ uint<64> offset;
+ } register: ty == 9;
+ } value;
+};
+
+type II_inline_instance = struct {
+ uint<64> insn_offset;
+ uint<32> type_id;
+ uint<16> param_count;
+ offset<uint<32>, B>[param_count] param_offsets;
+};
+
+type II_Header = struct {
+ uint<16> magic == 0xeb9f;
+ uint<8> version;
+ uint<8> flags;
+ uint<32> header_size;
+ offset<uint<32>, B> inline_info_offset;
+ offset<uint<32>, B> inline_info_size;
+ offset<uint<32>, B> location_offset;
+ offset<uint<32>, B> location_size;
+};
+
+type II = struct {
+ II_Header hdr;
+ II_inline_instance[hdr.inline_info_size] inline_instances @ hdr.inline_info_offset;
+ II_location_op[hdr.location_size] locations @ hdr.location_offset;
+};
+
+if (!poke_interactive_p) {
+ var inline_file = open("/tmp/inline_expansions.btf", IOS_M_RDONLY);
+ var inline_info = II @ inline_file : 0#B;
+
+ for (exp in inline_info.inline_instances) {
+ printf ("{low_pc => '0x%u64x',type_id => %u32d}\n", exp.insn_offset, exp.type_id);
+ }
+}
diff --git a/inline_encoder.c b/inline_encoder.c
new file mode 100644
index 0000000000000000000000000000000000000000..b4a681bd996088109ddf0a8167690ce83026324b
--- /dev/null
+++ b/inline_encoder.c
@@ -0,0 +1,496 @@
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2025 Facebook
+
+ Derived from ctf_encoder.c, which is:
+
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ Copyright (C) Red Hat Inc
+ */
+#include <bpf/btf.h>
+
+#include "dutil.h"
+#include "dwarves.h"
+#include "inline_encoder.h"
+#include "list.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+enum loc_type {
+ LOC_END_OF_EXPR,
+ LOC_SIGNED_CONST_1,
+ LOC_SIGNED_CONST_2,
+ LOC_SIGNED_CONST_4,
+ LOC_SIGNED_CONST_8,
+ LOC_UNSIGNED_CONST_1,
+ LOC_UNSIGNED_CONST_2,
+ LOC_UNSIGNED_CONST_4,
+ LOC_UNSIGNED_CONST_8,
+ LOC_REGISTER,
+} __attribute__((packed));
+
+struct loc {
+ enum loc_type type;
+ uint8_t size;
+};
+
+struct inline_parameter {
+ struct loc *location[16];
+};
+
+struct inline_instance {
+ struct list_head node;
+ uint64_t die_offset;
+ const char *name;
+ uint64_t insn_offset;
+ type_id_t type_id;
+ uint16_t param_count;
+ struct inline_parameter parameters[];
+};
+
+struct inline_encoder {
+ struct btf *btf;
+ struct cu *cu;
+ const char *source_filename;
+ const char *filename;
+ size_t inline_instance_cnt;
+
+ struct list_head inline_instances;
+};
+
+struct loc_node {
+ struct rb_node rb_node;
+ struct list_head node;
+ struct loc loc;
+};
+
+struct inline_encoder__header {
+ uint16_t magic;
+ uint8_t version;
+ uint8_t flags;
+ uint32_t header_size;
+ uint32_t inline_info_offset;
+ uint32_t inline_info_size;
+ uint32_t location_offset;
+ uint32_t location_size;
+};
+
+struct inline_encoder *inline_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load)
+{
+ struct inline_encoder *encoder = zalloc(sizeof(*encoder));
+
+ if (encoder) {
+ encoder->cu = cu;
+ encoder->source_filename = strdup(cu->filename);
+ encoder->filename = strdup(detached_filename ?: cu->filename);
+ encoder->btf = base_btf;
+ encoder->inline_instance_cnt = 0;
+
+ INIT_LIST_HEAD(&encoder->inline_instances);
+ }
+
+ return encoder;
+}
+
+void inline_encoder__delete(struct inline_encoder *encoder)
+{
+ if (encoder == NULL)
+ return;
+
+ zfree(&encoder->source_filename);
+ zfree(&encoder->filename);
+ encoder->btf = NULL; // Non-owning pointer to base BTF
+
+ struct inline_instance *exp, *n;
+ list_for_each_entry_safe_reverse(exp, n, &encoder->inline_instances, node) {
+ list_del_init(&exp->node);
+ for (size_t i = 0; i < exp->param_count; ++i)
+ for (size_t j = 0; j < 16; ++j)
+ free(exp->parameters[i].location[j]);
+ free(exp);
+ }
+
+ free(encoder);
+}
+
+static size_t inline_instance__sizeof(uint16_t param_count)
+{
+ return offsetof(struct inline_instance, parameters) + param_count * sizeof(struct inline_parameter);
+}
+
+static struct loc *loc__make_const(bool is_signed, size_t size, uint64_t value)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc) + size);
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->size = sizeof(struct loc) + size;
+ switch (size) {
+ case 1:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_1 : LOC_UNSIGNED_CONST_1;
+ break;
+ case 2:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_2 : LOC_UNSIGNED_CONST_2;
+ break;
+ case 4:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_4 : LOC_UNSIGNED_CONST_4;
+ break;
+ case 8:
+ new_loc->type = is_signed ? LOC_SIGNED_CONST_8 : LOC_UNSIGNED_CONST_8;
+ break;
+ default:
+ free(new_loc);
+ return NULL;
+ }
+ uint8_t *data = (uint8_t *)new_loc + sizeof(struct loc);
+ memcpy(data, &value, size);
+ return new_loc;
+}
+
+static struct loc *loc__make_register(uint8_t reg, int64_t offset)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc) + sizeof(reg) + sizeof(offset));
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->size = sizeof(struct loc) + sizeof(uint8_t) + sizeof(int64_t);
+ new_loc->type = LOC_REGISTER;
+ uint8_t *data = (uint8_t *)new_loc + sizeof(struct loc);
+ *data = reg;
+ data += sizeof(uint8_t);
+ memcpy(data, &offset, sizeof(int64_t));
+ return new_loc;
+}
+
+static struct loc *loc__make_eoe(void)
+{
+ struct loc *new_loc = zalloc(sizeof(*new_loc));
+ if (new_loc == NULL)
+ return NULL;
+ new_loc->type = LOC_END_OF_EXPR;
+ new_loc->size = sizeof(*new_loc);
+ return new_loc;
+}
+
+static uint32_t expr__size(struct loc *expr[16])
+{
+ uint32_t size = 0;
+ for (size_t i = 0; i < 16; ++i) {
+ if (expr[i] == NULL)
+ break;
+ size += expr[i]->size;
+ }
+ return size;
+}
+
+static int inline_encoder__encode_location(struct inline_encoder *encoder, struct location *loc, struct loc *expr[16])
+{
+ if (loc->expr == NULL && loc->exprlen == 0)
+ return 0;
+
+ if (loc->expr == NULL) {
+ expr[0] = loc__make_const(false, 8, loc->exprlen);
+ return 0;
+ }
+
+ size_t expr_i = 0;
+ for (size_t i = 0; i < loc->exprlen; ++i) {
+ Dwarf_Op op = loc->expr[i];
+ switch (op.atom) {
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 1, op.number);
+ break;
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 2, op.number);
+ break;
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 4, op.number);
+ break;
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_const1s, 8, op.number);
+ break;
+ case DW_OP_constu:
+ case DW_OP_consts:
+ expr[expr_i++] = loc__make_const(op.atom == DW_OP_consts, 8, op.number);
+ break;
+ case DW_OP_lit0 ... DW_OP_lit31:
+ expr[expr_i++] = loc__make_const(false, 1, op.atom - DW_OP_lit0);
+ break;
+ case DW_OP_reg0 ... DW_OP_reg31:
+ expr[expr_i++] = loc__make_register(op.atom - DW_OP_reg0, 0);
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31: {
+ expr[expr_i++] = loc__make_register(op.atom - DW_OP_breg0, op.number);
+ break;
+ }
+ case DW_OP_stack_value: {
+ // no-op
+ break;
+ }
+ default:
+ goto out_err;
+ }
+ if (expr_i >= 16)
+ goto out_err;
+ }
+
+ expr[expr_i++] = loc__make_eoe();
+ return 0;
+
+out_err:
+ for (size_t i = 0; i < expr_i; ++i) {
+ free(expr[i]);
+ expr[i] = NULL;
+ }
+
+ return -1;
+}
+
+static inline struct dwarf_tag *tag__dwarf(const struct tag *tag)
+{
+ uint8_t *data = (uint8_t *)tag;
+ return (struct dwarf_tag *)data;
+}
+
+static inline uint64_t die_offset(const struct tag *tag)
+{
+ uint8_t *data = (uint8_t *)tag;
+ data -= 64;
+ data += 24;
+ return *(uint64_t *)data;
+}
+
+static int inline_encoder__save_inline_expansion(struct inline_encoder *encoder, struct inline_expansion *exp)
+{
+ if (exp->name == NULL)
+ return 0;
+
+ struct inline_instance *instance = zalloc(inline_instance__sizeof(exp->nr_parms));
+ if (instance == NULL)
+ return -ENOMEM;
+
+ instance->die_offset = die_offset((struct tag *)exp);
+ instance->name = exp->name;
+ instance->insn_offset = exp->ip.addr;
+ instance->type_id = -1;
+ instance->param_count = exp->nr_parms;
+ // printf("inline instance for %u (%s): %lx %lx\n", instance->type_id, exp->name, instance->die_offset, instance->insn_offset);
+
+ uint32_t param_index = 0;
+ struct parameter *param = NULL;
+ list_for_each_entry(param, &exp->parms, tag.node) {
+ inline_encoder__encode_location(encoder, ¶m->location, instance->parameters[param_index++].location);
+ }
+
+ INIT_LIST_HEAD(&instance->node);
+ list_add_tail(&instance->node, &encoder->inline_instances);
+ encoder->inline_instance_cnt++;
+
+ return 0;
+}
+
+static int inline_encoder__encode_lexblock(struct inline_encoder *encoder, struct lexblock *lexblock, struct conf_load *conf_load)
+{
+ int err = 0;
+
+ struct tag *tag = NULL;
+ list_for_each_entry(tag, &lexblock->tags, node) {
+ if (tag->tag == DW_TAG_lexical_block) {
+ err = inline_encoder__encode_lexblock(encoder, tag__lexblock(tag), conf_load);
+ if (err)
+ goto out;
+ continue;
+ } else if (tag->tag != DW_TAG_inlined_subroutine) {
+ continue;
+ }
+
+ struct inline_expansion *exp = tag__inline_expansion(tag);
+ err = inline_encoder__save_inline_expansion(encoder, exp);
+ if (err)
+ goto out;
+ }
+
+ return err;
+
+out:
+ return err;
+}
+
+int inline_encoder__encode_cu(struct inline_encoder *encoder, struct cu *cu, struct conf_load *conf_load)
+{
+ int err = 0;
+
+ uint32_t fn_id;
+ struct function *fn;
+ cu__for_each_function(cu, fn_id, fn) {
+ if (fn->declaration)
+ continue;
+ if (function__addr(fn) == 0)
+ continue;
+
+ err = inline_encoder__encode_lexblock(encoder, &fn->lexblock, conf_load);
+ if (err)
+ goto out;
+ }
+ return err;
+out:
+ return err;
+}
+
+struct node_type_id {
+ const char *name;
+ type_id_t type_id;
+};
+
+static int cmpstrp(const void *left, const void *right)
+{
+ struct node_type_id *left_node = (struct node_type_id*)left;
+ struct node_type_id *right_node = (struct node_type_id*)right;
+ return strcmp(left_node->name, right_node->name);
+}
+
+static struct node_type_id *inline_encoder__build_type_id_cache(struct inline_encoder *encoder)
+{
+ struct node_type_id *exps = (struct node_type_id*)malloc(encoder->inline_instance_cnt * sizeof(struct node_type_id));
+
+ size_t exp_id = 0;
+ struct inline_instance *exp;
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ exps[exp_id++] = (struct node_type_id){exp->name, 0};
+ }
+
+ qsort(exps, encoder->inline_instance_cnt, sizeof(struct node_type_id), cmpstrp);
+
+ return exps;
+}
+
+static int inline_encoder__write_raw_file(struct inline_encoder *encoder, const char *filename)
+{
+ int err = -1;
+
+ int fd = creat(filename, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ fprintf(stderr, "%s open(%s) failed!\n", __func__, filename);
+ goto out;
+ }
+
+ struct inline_encoder__header header = {
+ .magic = 0xeb9f,
+ .version = 1,
+ .flags = 0,
+ .header_size = sizeof(header),
+ .inline_info_offset = sizeof(struct inline_encoder__header),
+ .inline_info_size = 0,
+ .location_offset = 0,
+ .location_size = 2,
+ };
+ write(fd, &header, header.header_size);
+
+ struct node_type_id *type_id_cache = inline_encoder__build_type_id_cache(encoder);
+
+ struct inline_instance *exp;
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ struct node_type_id lookup_key = { exp->name, 0 };
+ struct node_type_id *found = bsearch(&lookup_key, type_id_cache, encoder->inline_instance_cnt, sizeof(lookup_key), cmpstrp);
+ assert(found != NULL);
+ if (found->type_id == 0) {
+ int type_id = btf__find_by_name_kind(encoder->btf, exp->name, BTF_KIND_FUNC);
+ found->type_id = (type_id < 0) ? -1 : type_id;
+ }
+ exp->type_id = found->type_id;
+ if (exp->type_id == -1) continue;
+
+ const void *data = exp;
+ header.inline_info_size += write(
+ fd,
+ data + offsetof(struct inline_instance, insn_offset),
+ inline_instance__sizeof(0)
+ - offsetof(struct inline_instance, insn_offset)
+ - 2); // Skip padding at the end of the struct
+ for (uint16_t i = 0; i < exp->param_count; ++i) {
+ struct inline_parameter *param = &exp->parameters[i];
+ if (param->location[0] == NULL) {
+ uint32_t zero = 0;
+ header.inline_info_size += write(fd, &zero, sizeof(zero));
+ } else {
+ header.inline_info_size += write(fd, &header.location_size, sizeof(header.location_size));
+ header.location_size += expr__size(param->location);
+ }
+ }
+ }
+
+ free(type_id_cache);
+
+ struct loc end_of_expr = {
+ .type = LOC_END_OF_EXPR,
+ .size = sizeof(end_of_expr),
+ };
+ write(fd, &end_of_expr, sizeof(end_of_expr));
+ list_for_each_entry(exp, &encoder->inline_instances, node) {
+ if (exp->type_id == -1)
+ continue;
+ for (uint16_t i = 0; i < exp->param_count; ++i) {
+ struct inline_parameter *param = &exp->parameters[i];
+ for (size_t j = 0; j < 16; ++j) {
+ struct loc *op = param->location[j];
+ if (op == NULL)
+ break;
+ write(fd, op, op->size);
+ }
+ }
+ }
+ header.location_offset = header.inline_info_offset + header.inline_info_size;
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &header, header.header_size);
+
+ close(fd);
+ return 0;
+
+out:
+ if (fd != - 1)
+ close(fd);
+ unlink(filename);
+ return err;
+}
+
+int inline_encoder__encode(struct inline_encoder *encoder, struct conf_load *conf_load)
+{
+ char tmp_fn[PATH_MAX];
+ snprintf(tmp_fn, sizeof(tmp_fn), "%s.btf_inline", encoder->filename);
+
+ int err = inline_encoder__write_raw_file(encoder, tmp_fn);
+ if (err) return err;
+
+ const char *llvm_objcopy = getenv("LLVM_OBJCOPY");
+ if (!llvm_objcopy)
+ llvm_objcopy = "llvm-objcopy";
+
+ char cmd[PATH_MAX * 2];
+ snprintf(cmd, sizeof(cmd), "%s --add-section .BTF_inline=%s %s",
+ llvm_objcopy, tmp_fn, encoder->filename);
+ if (system(cmd)) {
+ fprintf(stderr, "%s: failed to add .BTF_inline section '%s': %d!\n",
+ __func__, tmp_fn, errno);
+ err = -1;
+ goto unlink;
+ }
+
+ err = 0;
+unlink:
+ unlink(tmp_fn);
+ return err;
+}
+
+void inline_encoder__set_btf(struct inline_encoder *encoder, struct btf *btf)
+{
+ encoder->btf = btf;
+}
diff --git a/inline_encoder.h b/inline_encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3472d011a56e84efc77d85fcee1a6aa88204c86
--- /dev/null
+++ b/inline_encoder.h
@@ -0,0 +1,25 @@
+#ifndef _INLINE_ENCODER_H_
+#define _INLINE_ENCODER_H_ 1
+/*
+ SPDX-License-Identifier: GPL-2.0-only
+
+ Copyright (C) 2025 Facebook
+
+ Derived from btf_encoder.h, which is:
+ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+#include <stdbool.h>
+
+struct inline_encoder;
+struct conf_load;
+struct btf;
+struct cu;
+
+struct inline_encoder *inline_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load);
+void inline_encoder__delete(struct inline_encoder *encoder);
+int inline_encoder__encode_cu(struct inline_encoder *encoder, struct cu *cu, struct conf_load *conf_load);
+int inline_encoder__encode(struct inline_encoder *encoder, struct conf_load *conf_load);
+
+void inline_encoder__set_btf(struct inline_encoder *encoder, struct btf *btf);
+
+#endif /* _INLINE_ENCODER_H_ */
diff --git a/pahole.c b/pahole.c
index af3e1cfe224dd14a5be2c072461505cf3cc387c5..093d76478dbd5a687c44b0f19fce73cfe6aae161 100644
--- a/pahole.c
+++ b/pahole.c
@@ -28,12 +28,15 @@
#include "dutil.h"
//#include "ctf_encoder.h" FIXME: disabled, probably its better to move to Oracle's libctf
#include "btf_encoder.h"
+#include "inline_encoder.h"
static struct btf_encoder *btf_encoder;
+static struct inline_encoder *inline_encoder;
static char *detached_btf_filename;
struct cus *cus;
static bool btf_encode;
static bool ctf_encode;
+static bool inline_encode;
static bool sort_output;
static bool need_resort;
static bool first_obj_only;
@@ -1638,6 +1641,11 @@ static const struct argp_option pahole__options[] = {
.key = 'J',
.doc = "Encode as BTF",
},
+ {
+ .name = "inline_encode",
+ .key = 'L',
+ .doc = "Encode inline expansions into .BTF.inline section",
+ },
{
.name = "btf_encode_detached",
.key = ARGP_btf_encode_detached,
@@ -1832,6 +1840,7 @@ static error_t pahole__options_parser(int key, char *arg,
break;
case ARGP_btf_encode_detached:
detached_btf_filename = arg; // fallthru
+ case 'L': inline_encode = inline_encode || key == 'L'; // fallthru
case 'J': btf_encode = 1;
conf_load.get_addr_info = true;
conf_load.ignore_alignment_attr = true;
@@ -3140,6 +3149,22 @@ static enum load_steal_kind pahole_stealer__btf_encode(struct cu *cu, struct con
return LSK__STOP_LOADING;
}
+ if (inline_encode) {
+ if (!inline_encoder)
+ inline_encoder = inline_encoder__new(cu, detached_btf_filename, conf_load->base_btf, global_verbose, conf_load);
+
+ if (!inline_encoder) {
+ fprintf(stderr, "Error creating inline encoder.\n");
+ return LSK__STOP_LOADING;
+ }
+
+ err = inline_encoder__encode_cu(inline_encoder, cu, conf_load);
+ if (err < 0) {
+ fprintf(stderr, "Error while encoding BTF.inline.\n");
+ return LSK__STOP_LOADING;
+ }
+ }
+
return LSK__DELETE;
}
@@ -3672,10 +3697,19 @@ try_sole_arg_as_class_names:
if (btf_encode && btf_encoder) { // maybe all CUs were filtered out and thus we don't have an encoder?
err = btf_encoder__encode(btf_encoder, &conf_load);
- btf_encoder__delete(btf_encoder);
if (err) {
fputs("Failed to encode BTF\n", stderr);
- goto out_cus_delete;
+ goto out_btf_encoder_delete;
+ }
+
+ if (inline_encode && inline_encoder) {
+ inline_encoder__set_btf(inline_encoder, btf_encoder__btf(btf_encoder));
+ err = inline_encoder__encode(inline_encoder, &conf_load);
+ inline_encoder__delete(inline_encoder);
+ if (err) {
+ fputs("Failed to encode BTF.inline\n", stderr);
+ goto out_btf_encoder_delete;
+ }
}
}
out_ok:
@@ -3683,6 +3717,8 @@ out_ok:
print_stats();
rc = EXIT_SUCCESS;
+out_btf_encoder_delete:
+ btf_encoder__delete(btf_encoder);
out_cus_delete:
#ifdef DEBUG_CHECK_LEAKS
cus__delete(cus);
--
2.47.1
next prev parent reply other threads:[~2025-04-16 19:21 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-16 19:20 [PATCH RFC 0/3] list inline expansions in .BTF.inline Thierry Treyer via B4 Relay
2025-04-16 19:20 ` Thierry Treyer
2025-04-16 19:20 ` [PATCH RFC 1/3] dwarf_loader: Add parameters list to inlined expansion Thierry Treyer via B4 Relay
2025-04-16 19:20 ` Thierry Treyer
2025-04-16 19:20 ` [PATCH RFC 2/3] dwarf_loader: Add name to inline expansion Thierry Treyer via B4 Relay
2025-04-16 19:20 ` Thierry Treyer
2025-04-16 19:20 ` Thierry Treyer via B4 Relay [this message]
2025-04-16 19:20 ` [PATCH RFC 3/3] inline_encoder: Introduce inline encoder to emit BTF.inline Thierry Treyer
2025-04-25 18:40 ` [PATCH RFC 0/3] list inline expansions in .BTF.inline Daniel Xu
2025-04-28 20:51 ` Alexei Starovoitov
2025-04-29 19:14 ` Thierry Treyer
2025-04-29 23:58 ` Alexei Starovoitov
2025-04-30 15:25 ` Alan Maguire
2025-05-01 19:38 ` Thierry Treyer
2025-05-02 8:31 ` Alan Maguire
2025-05-19 12:02 ` Alan Maguire
2025-05-22 17:56 ` Thierry Treyer
2025-05-22 20:03 ` Andrii Nakryiko
2025-05-22 20:16 ` Eduard Zingerman
2025-05-23 18:57 ` Thierry Treyer
2025-05-26 14:30 ` Alan Maguire
2025-05-27 21:41 ` Andrii Nakryiko
2025-05-28 10:14 ` Alan Maguire
2025-05-23 17:33 ` Alexei Starovoitov
2025-05-23 18:35 ` Thierry Treyer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250416-btf_inline-v1-3-e4bd2f8adae5@meta.com \
--to=devnull+ttreyer.meta.com@kernel.org \
--cc=acme@kernel.org \
--cc=alan.maguire@oracle.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=dwarves@vger.kernel.org \
--cc=ihor.solodrai@linux.dev \
--cc=mykolal@meta.com \
--cc=songliubraving@meta.com \
--cc=ttreyer@meta.com \
--cc=yhs@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.