From patchwork Wed Apr 16 19:20:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Treyer X-Patchwork-Id: 14054428 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AD6F5217712; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; cv=none; b=sL8R2+ZnnqqfqKpCiJFfKzLO4c/Qc/MCO8c2R+eDISpDiFXA9BeCY3XG5HZGhWP1B+35mMNBUGq7i3D79BbOKSyyA1QcTXSoM4PTp79bAadLh0WbCWCVKkPnWZpimHYepC7METpiKwpMe9zaw1t5OpBuQ11eJZ1bo6lOGSEZJNg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; c=relaxed/simple; bh=Lhtpn16V8YwbUjT/n7MhlvePq4SQzUPVC5K3c/nMgvQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bhuoBXb7fq1T4QLe17IVLtHzYVdP2Fo0tuRyEdyAcFuGVUboWheNvQ1kI+a9xpuYb4QNbrtTYaipznQXhYA32XVp4M11robbukrhdM0bquckNQqCAErP50ozOwgxUJIQm0z9ONV3CnA6aRWmBpMAdps6BcnCXAQXET/RLDUGez0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=T9w+gBfb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="T9w+gBfb" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3D8C7C4CEED; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744831267; bh=Lhtpn16V8YwbUjT/n7MhlvePq4SQzUPVC5K3c/nMgvQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=T9w+gBfbEJzoCShNHtWHj5nwsHgku1y6L6kCA7Y6t3Len5XpsNCPMoBIK+Mcfkl0o VLRannn0ehMqhRMn5AIynizlRwj3xSf/fLkp7GC/EM8izEsO4YLRF6sW4ZfL/HodwP i2McqjtKjiepuWOQoltZUsTrt688iea7/WfnVVNzbKH7ilkaG22h/2wr/+r7ffCP+d pPzIq6XIT2gEzyOQHrnfp51ETxnY9X/cZtyWnX8X85zn7giMqneU2daZqHSZlrBgzu kkolXALvO4naW7DOhisDHi/30AHCo2zYCPYrn6YDf8ZMB2RKlYXNEjS/BuO/IPLFE+ OjXdHIz9xcaCw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 296ACC369C9; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Date: Wed, 16 Apr 2025 19:20:35 +0000 Subject: [PATCH RFC 1/3] dwarf_loader: Add parameters list to inlined expansion Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250416-btf_inline-v1-1-e4bd2f8adae5@meta.com> References: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> In-Reply-To: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> To: dwarves@vger.kernel.org, bpf@vger.kernel.org Cc: Thierry Treyer , 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 X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1744831265; l=14299; i=ttreyer@meta.com; s=20250416; h=from:subject:message-id; bh=KRjj5XcMWGamVrhTIaliZ44bd3FXiuifuRRXXxEDL9M=; b=HfDpNEV/AgTaSWbxB+jOOb1YjMUwUgqpUlHK/+RVnS1TS8fc1NClrGyda5lWXgbdFFYZbSHxa bEapQs9u2LUDKlxf9ryM7tAxoPv0BV358xKfhpbwpGbR+aJIusk8B47 X-Developer-Key: i=ttreyer@meta.com; a=ed25519; pk=2NAyAkZ6zhou7+5zqr5ikv3g5BfFbkznGzvzfKv1nbU= X-Endpoint-Received: by B4 Relay for ttreyer@meta.com/20250416 with auth_id=382 X-Original-From: Thierry Treyer Reply-To: ttreyer@meta.com From: Thierry Treyer X-Patchwork-State: RFC From: Thierry Treyer The parameters of inlined expansions are stored in the lexblock tag list, making it difficult to iterate over the parameters of a given inlined expansion. Add a list of parameters to 'struct inline_expansion', so the parameters are stored in their expansion instead of the lexblock. Add location to 'struct parameter', so their location expression can be referenced when consuming the DWARF. Parameters with a constant value have their value stored in the location too: 'location.expr' is set to NULL and 'location.exprlen' is set to the constant's value. Signed-off-by: Thierry Treyer --- dwarf_loader.c | 167 ++++++++++++++++++++++++++++++++++----------------------- dwarves.c | 26 +++++++++ dwarves.h | 6 +++ 3 files changed, 132 insertions(+), 67 deletions(-) diff --git a/dwarf_loader.c b/dwarf_loader.c index 84122d04e42cab53c5598ae62b750a43e187e11d..24ac9afceb3793c165d3e92cfdfaf27ab67fd4d6 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1297,6 +1297,8 @@ static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, parm->has_loc = dwarf_attr(die, DW_AT_location, &attr) != NULL; if (parm->has_loc) { + attr_location(die, &parm->location.expr, &parm->location.exprlen); + int expected_reg = cu->register_params[param_idx]; int actual_reg = parameter__reg(&attr, expected_reg); @@ -1312,6 +1314,8 @@ static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, parm->unexpected_reg = 1; } else if (has_const_value) { parm->optimized = 1; + parm->location.expr = NULL; + parm->location.exprlen = attr_numeric(die, DW_AT_const_value); } } @@ -1374,6 +1378,8 @@ static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu dwarf_tag__set_attr_type(dtag, type, die, DW_AT_abstract_origin); exp->ip.addr = 0; exp->high_pc = 0; + exp->nr_parms = 0; + INIT_LIST_HEAD(&exp->parms); if (!cu->has_addr_info) goto out; @@ -1794,6 +1800,7 @@ static struct tag *die__create_new_string_type(Dwarf_Die *die, struct cu *cu) static struct tag *die__create_new_parameter(Dwarf_Die *die, struct ftype *ftype, struct lexblock *lexblock, + struct inline_expansion *exp, struct cu *cu, struct conf_load *conf, int param_idx) { @@ -1808,15 +1815,16 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) return NULL; } + } else if (exp != NULL) { + /* + * Inline expansion stores the parameters in a list to emit + * .BTF_inline parameter location. + */ + inline_expansion__add_parameter(exp, parm); } else { /* - * DW_TAG_formal_parameters on a non DW_TAG_subprogram nor - * DW_TAG_subroutine_type tag happens sometimes, likely due to - * compiler optimizing away a inline expansion (at least this - * was observed in some cases, such as in the Linux kernel - * current_kernel_time function circa 2.6.20-rc5), keep it in - * the lexblock tag list because it can be referenced as an - * DW_AT_abstract_origin in another DW_TAG_formal_parameter. + * Keep it in the lexblock tag list because it can be referenced + * as an DW_AT_abstract_origin in another DW_TAG_formal_parameter. */ lexblock__add_tag(lexblock, &parm->tag); } @@ -1884,7 +1892,7 @@ static struct tag *die__create_new_subroutine_type(Dwarf_Die *die, tag__print_not_supported(die); continue; case DW_TAG_formal_parameter: - tag = die__create_new_parameter(die, ftype, NULL, cu, conf, -1); + tag = die__create_new_parameter(die, ftype, NULL, NULL, cu, conf, -1); break; case DW_TAG_unspecified_parameters: ftype->unspec_parms = 1; @@ -2136,10 +2144,13 @@ static struct tag *die__create_new_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu, struct conf_load *conf); -static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu, struct conf_load *conf) +static int die__process_inline_expansion(Dwarf_Die *die, + struct inline_expansion *exp, struct lexblock *lexblock, + struct cu *cu, struct conf_load *conf) { Dwarf_Die child; struct tag *tag; + int parm_idx = 0; if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) return 0; @@ -2179,6 +2190,7 @@ static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblo * * cu__tag_not_handled(cu, die); */ + tag = die__create_new_parameter(die, NULL, lexblock, exp, cu, conf, parm_idx++); continue; case DW_TAG_inlined_subroutine: tag = die__create_new_inline_expansion(die, lexblock, cu, conf); @@ -2230,7 +2242,7 @@ static struct tag *die__create_new_inline_expansion(Dwarf_Die *die, if (exp == NULL) return NULL; - if (die__process_inline_expansion(die, lexblock, cu, conf) != 0) { + if (die__process_inline_expansion(die, exp, lexblock, cu, conf) != 0) { tag__free(&exp->ip.tag, cu); return NULL; } @@ -2315,7 +2327,7 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, continue; } case DW_TAG_formal_parameter: - tag = die__create_new_parameter(die, ftype, lexblock, cu, conf, param_idx++); + tag = die__create_new_parameter(die, ftype, lexblock, NULL, cu, conf, param_idx++); break; case DW_TAG_variable: tag = die__create_new_variable(die, cu, conf, 0); @@ -2607,54 +2619,87 @@ static void __tag__print_abstract_origin_not_found(struct tag *tag, #define tag__print_abstract_origin_not_found(tag) \ __tag__print_abstract_origin_not_found(tag, __func__, __LINE__) +static void parameter__recode_dwarf_type(struct parameter *parm, struct cu *cu) +{ + struct dwarf_cu *dcu = cu->priv; + struct dwarf_tag *dparm = tag__dwarf(&parm->tag); + + if (dparm->type == 0) { + if (dparm->abstract_origin == 0) { + /* Function without parameters */ + parm->tag.type = 0; + return; + } + // FIXME: Should this use find_type_by_ref instead? + struct dwarf_tag *dtype = dwarf_cu__find_tag_by_ref(dcu, dparm, abstract_origin); + if (dtype == NULL) { + tag__print_abstract_origin_not_found(&parm->tag); + return; + } + struct parameter *oparm = tag__parameter(dtag__tag(dtype)); + parm->name = oparm->name; + parm->tag.type = dtag__tag(dtype)->type; + /* Share location information between parameter and abstract origin; + * if neither have location, we will mark the parameter as optimized out. + * Also share info regarding unexpected register use for parameters. + */ + if (parm->has_loc) + oparm->has_loc = parm->has_loc; + + if (parm->optimized) + oparm->optimized = parm->optimized; + if (parm->unexpected_reg) + oparm->unexpected_reg = parm->unexpected_reg; + return; + } + + struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(dcu, dparm, type); + if (dtype == NULL) { + tag__print_type_not_found(&parm->tag); + return; + } + parm->tag.type = dtype->small_id; +} + static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu) { struct parameter *pos; - struct dwarf_cu *dcu = cu->priv; struct ftype *type = tag__ftype(tag); - ftype__for_each_parameter(type, pos) { - struct dwarf_tag *dpos = tag__dwarf(&pos->tag); - struct parameter *opos; - struct dwarf_tag *dtype; - - if (dpos->type == 0) { - if (dpos->abstract_origin == 0) { - /* Function without parameters */ - pos->tag.type = 0; - continue; - } - dtype = dwarf_cu__find_tag_by_ref(dcu, dpos, abstract_origin); - if (dtype == NULL) { - tag__print_abstract_origin_not_found(&pos->tag); - continue; - } - opos = tag__parameter(dtag__tag(dtype)); - pos->name = opos->name; - pos->tag.type = dtag__tag(dtype)->type; - /* share location information between parameter and - * abstract origin; if neither have location, we will - * mark the parameter as optimized out. Also share - * info regarding unexpected register use for - * parameters. - */ - if (pos->has_loc) - opos->has_loc = pos->has_loc; + ftype__for_each_parameter(type, pos) + parameter__recode_dwarf_type(pos, cu); +} - if (pos->optimized) - opos->optimized = pos->optimized; - if (pos->unexpected_reg) - opos->unexpected_reg = pos->unexpected_reg; - continue; - } +static void inline_expansion__recode_dwarf_types(struct tag *tag, struct cu *cu) +{ + struct dwarf_cu *dcu = cu->priv; + struct dwarf_tag *dtag = tag__dwarf(tag); - dtype = dwarf_cu__find_type_by_ref(dcu, dpos, type); - if (dtype == NULL) { - tag__print_type_not_found(&pos->tag); - continue; - } - pos->tag.type = dtype->small_id; + /* DW_TAG_inlined_subroutine is an special case as dwarf_tag->id is + * in fact an abtract origin, i.e. must be looked up in the tags_table, + * not in the types_table. + */ + struct dwarf_tag *ftype = NULL; + if (dtag->type != 0) + ftype = dwarf_cu__find_tag_by_ref(dcu, dtag, type); + else + ftype = dwarf_cu__find_tag_by_ref(dcu, dtag, abstract_origin); + if (ftype == NULL) { + if (dtag->type != 0) + tag__print_type_not_found(tag); + else + tag__print_abstract_origin_not_found(tag); + return; } + + // TODO: Is ftype a prototype or a function? + ftype__recode_dwarf_types(dtag__tag(ftype), cu); + + struct tag *pos; + struct inline_expansion *exp = tag__inline_expansion(tag); + list_for_each_entry(pos, &exp->parms, node) + parameter__recode_dwarf_type(tag__parameter(pos), cu); + exp->ip.tag.type = ftype->small_id; } static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu) @@ -2671,18 +2716,7 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu) lexblock__recode_dwarf_types(tag__lexblock(pos), cu); continue; case DW_TAG_inlined_subroutine: - if (dpos->type != 0) - dtype = dwarf_cu__find_tag_by_ref(dcu, dpos, type); - else - dtype = dwarf_cu__find_tag_by_ref(dcu, dpos, abstract_origin); - if (dtype == NULL) { - if (dpos->type != 0) - tag__print_type_not_found(pos); - else - tag__print_abstract_origin_not_found(pos); - continue; - } - ftype__recode_dwarf_types(dtag__tag(dtype), cu); + inline_expansion__recode_dwarf_types(pos, cu); continue; case DW_TAG_formal_parameter: @@ -2862,12 +2896,11 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu) case DW_TAG_namespace: return namespace__recode_dwarf_types(tag, cu); - /* Damn, DW_TAG_inlined_subroutine is an special case - as dwarf_tag->id is in fact an abtract origin, i.e. must be - looked up in the tags_table, not in the types_table. - The others also point to routines, so are in tags_table */ case DW_TAG_inlined_subroutine: + inline_expansion__recode_dwarf_types(tag, cu); + return 0; case DW_TAG_imported_module: + /* Modules point to routines, so are in tags_table */ dtype = dwarf_cu__find_tag_by_ref(cu->priv, dtag, type); goto check_type; /* Can be for both types and non types */ diff --git a/dwarves.c b/dwarves.c index ef93239d26827711e23b405dd113986867d18507..2232ee713d57a92a700be56be7b7e1128741e24a 100644 --- a/dwarves.c +++ b/dwarves.c @@ -203,6 +203,20 @@ void formal_parameter_pack__delete(struct formal_parameter_pack *pack, struct cu cu__tag_free(cu, &pack->tag); } +static void parameter__delete(struct parameter *parm, struct cu *cu); + +static void inline_expansion__delete(struct inline_expansion *exp, struct cu *cu) +{ + if (exp == NULL) + return; + + struct tag *parm, *n; + list_for_each_entry_safe_reverse(parm, n, &exp->parms, node) { + list_del_init(&parm->node); + parameter__delete(tag__parameter(parm), cu); + } +} + void tag__delete(struct tag *tag, struct cu *cu) { if (tag == NULL) @@ -225,6 +239,8 @@ void tag__delete(struct tag *tag, struct cu *cu) ftype__delete(tag__ftype(tag), cu); break; case DW_TAG_subprogram: function__delete(tag__function(tag), cu); break; + case DW_TAG_inlined_subroutine: + inline_expansion__delete(tag__inline_expansion(tag), cu); break; case DW_TAG_lexical_block: lexblock__delete(tag__lexblock(tag), cu); break; case DW_TAG_GNU_template_parameter_pack: @@ -1514,6 +1530,12 @@ void formal_parameter_pack__add(struct formal_parameter_pack *pack, struct param list_add_tail(¶m->tag.node, &pack->params); } +void inline_expansion__add_parameter(struct inline_expansion *exp, struct parameter *parm) +{ + ++exp->nr_parms; + list_add_tail(&parm->tag.node, &exp->parms); +} + void lexblock__add_tag(struct lexblock *block, struct tag *tag) { list_add_tail(&tag->node, &block->tags); @@ -2116,6 +2138,10 @@ static int list__for_all_tags(struct list_head *list, struct cu *cu, if (list__for_all_tags(&tag__lexblock(pos)->tags, cu, iterator, cookie)) return 1; + } else if (pos->tag == DW_TAG_inlined_subroutine) { + if (list__for_all_tags(&tag__inline_expansion(pos)->parms, + cu, iterator, cookie)) + return 1; } if (iterator(pos, cu, cookie)) diff --git a/dwarves.h b/dwarves.h index 8234e1a09128c667a2a516dccb6d7c6a194f8c5b..38efd6a6e2b0b0e5a571a8d12bbec33502509d8b 100644 --- a/dwarves.h +++ b/dwarves.h @@ -811,6 +811,8 @@ struct inline_expansion { struct ip_tag ip; size_t size; uint64_t high_pc; + struct list_head parms; + uint16_t nr_parms; }; static inline struct inline_expansion * @@ -819,6 +821,9 @@ static inline struct inline_expansion * return (struct inline_expansion *)tag; } +struct parameter; +void inline_expansion__add_parameter(struct inline_expansion *exp, struct parameter *parm); + struct label { struct ip_tag ip; const char *name; @@ -929,6 +934,7 @@ size_t lexblock__fprintf(const struct lexblock *lexblock, const struct cu *cu, struct parameter { struct tag tag; const char *name; + struct location location; uint8_t optimized:1; uint8_t unexpected_reg:1; uint8_t has_loc:1; From patchwork Wed Apr 16 19:20:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Treyer X-Patchwork-Id: 14054430 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6B32224220; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; cv=none; b=aK7y5i64tZIcAY/fESbOEqaCccBBtpct4n7QAjbEqTNr/H0ezNhMQFspzRzYuAPF96UlvnVFd6eTuwqEugAiHQQs8uvhYj3opixOhQHqXXib5cAAHop/tKiiJDjjmm/0j1xd8rDV7SchSpmK8aRE5yc1h5O2IAe3hzVtPb6KOy0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; c=relaxed/simple; bh=JdyDlfKzAPlsDcZj5fiWk0k/AOqsgufuJ/1KexX8/LU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hZimrgsab3ij6IVM9oVizR0L3mzrw4FXnLaZYlJcFeQ3/WE6oq8uD/ubNqVSqpbLC2rlfHg35lPfsROH9SlXYpbngR1lfhmivA5v8KOc7Kn9PNgbLwMhior3dpRtrWLH7HDeuRzAaCvvW/OejPlba5oB7yGg0vpGaolYIeW1yvk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=R2xsbCnQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="R2xsbCnQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 50B6AC4CEE4; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744831267; bh=JdyDlfKzAPlsDcZj5fiWk0k/AOqsgufuJ/1KexX8/LU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=R2xsbCnQp5oKl5os8qln0RvXnzCl8wUb48b2MKlGoOV3zVptGHpCkaeGZ6VIl+4dL z2BSUJoJm/GS/YFvpxNBfyseCBjsCKXsaJXoM61Va1GB4MLa1EFZd+lFc78z3pbn9T IwpPMk5QeGefKJOLAxthuCR1lC/yhR1Y6XWT/Krj1zNfo64tFp+mk/Z38Zog8f1c2g KTP484vdEl5V/FDLtCc5Y40BJwknoVNh+8Tt7dhyvWGyNrRM56AHVY3gErxqnBqqb8 m+Y2F2qtFYpTpZ3vcXyqw50YpKgAohqycWCHLpno1dbrK2n9JrGMQ/iCXfV9JU1CD8 LwNTsjUBmx8UQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3ECDAC369C7; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Date: Wed, 16 Apr 2025 19:20:36 +0000 Subject: [PATCH RFC 2/3] dwarf_loader: Add name to inline expansion Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250416-btf_inline-v1-2-e4bd2f8adae5@meta.com> References: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> In-Reply-To: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> To: dwarves@vger.kernel.org, bpf@vger.kernel.org Cc: Thierry Treyer , 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 X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1744831265; l=1535; i=ttreyer@meta.com; s=20250416; h=from:subject:message-id; bh=GxSE1f8+lYV3zrhaWnIMYQRjICGOXJevdGgMRZAvrV4=; b=hFKyHffdY9Kr4yAK5v4Mo4gSpBq0a9f1W8sNS5wWj//EVX9husdcweWFfcGUHpYs/TXgpJ16d Bw9nKFhKN5LBueMspRyHu1ZwbRwtbiypjIQW2oIM6KdjNqdguG1bfXy X-Developer-Key: i=ttreyer@meta.com; a=ed25519; pk=2NAyAkZ6zhou7+5zqr5ikv3g5BfFbkznGzvzfKv1nbU= X-Endpoint-Received: by B4 Relay for ttreyer@meta.com/20250416 with auth_id=382 X-Original-From: Thierry Treyer Reply-To: ttreyer@meta.com From: Thierry Treyer X-Patchwork-State: RFC From: Thierry Treyer Add the name field to inline expansion. It contains the name of the abstract origin. The name can be used to locate the origin function in the BTF. Surely there's a better way to find the btf_id of the original function? Signed-off-by: Thierry Treyer --- dwarf_loader.c | 9 +++++++++ dwarves.h | 1 + 2 files changed, 10 insertions(+) diff --git a/dwarf_loader.c b/dwarf_loader.c index 24ac9afceb3793c165d3e92cfdfaf27ab67fd4d6..d7a130d138a65bad71a563cf3d629bdd7b9e6c6f 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1376,6 +1376,15 @@ static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu dtag->decl_file = attr_string(die, DW_AT_call_file, conf); dtag->decl_line = attr_numeric(die, DW_AT_call_line); dwarf_tag__set_attr_type(dtag, type, die, DW_AT_abstract_origin); + + Dwarf_Attribute attr_orig; + if (dwarf_attr(die, DW_AT_abstract_origin, &attr_orig)) { + Dwarf_Die die_orig; + if (dwarf_formref_die(&attr_orig, &die_orig)) { + exp->name = attr_string(&die_orig, DW_AT_name, conf); + } + } + exp->ip.addr = 0; exp->high_pc = 0; exp->nr_parms = 0; diff --git a/dwarves.h b/dwarves.h index 38efd6a6e2b0b0e5a571a8d12bbec33502509d8b..13b19433fe4aa7cd54ec0160c7008a33cbc17e24 100644 --- a/dwarves.h +++ b/dwarves.h @@ -809,6 +809,7 @@ struct ip_tag { struct inline_expansion { struct ip_tag ip; + const char *name; size_t size; uint64_t high_pc; struct list_head parms; From patchwork Wed Apr 16 19:20:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Treyer X-Patchwork-Id: 14054429 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6ACF22371A; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; cv=none; b=IgFOzSm3FfDNSONvlq4XiPZ++WJcDE6AFFbYJK13arL+gQV3BzwlxXsWnAn/QchBvIwzqQ8ysAZ7tplQ8MM5TjCbkvskWF2kKQZ6Z+zLEF0fMvVzz58Ssq/cTwNUTfgm9Dlc1mq/jorQgSw+XFezjrQEbk4t1ESgACC9We2IK6s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744831267; c=relaxed/simple; bh=a+GP7KFkY1hXZ0JBGWWVGl4cLU0RDSxrkKRm3VSbn6c=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GbMjxc/lx4H0EoGasa+ZmJA55qk//HOGI6IpqaPD1U41pE7W96AH9Nt4YVQsSwVie1gqOdwp8HV5P3OiyyyTYLCD+/npTmaPk+Xz3SUZOl+o2O1Bp1xWT5d28gSXJdRisTp4Sr/2N/Vwmncvv5CRuSAWUKNZuoPDQCHYRaBgk3A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=koKvo2OQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="koKvo2OQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6B600C4CEEC; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744831267; bh=a+GP7KFkY1hXZ0JBGWWVGl4cLU0RDSxrkKRm3VSbn6c=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=koKvo2OQFCt3eYHbBWo/BCgvqdVNMT+O+DyNBoUYEZF++P+KHDsusLaxt2zqKWQGj 3fO0UHPH6El4tD5+tYLUJBZzcPTJQZ9dG+eqzYxWo6thlUuf/iu58AavmmUmQ6eX9N jdOzAJMxX6pwS/fapnGhNoIYWJsXWqBNW2c9xJuz7CjciPWmxdHPdlj8BOeuxz8oMB u1J8Sa4wR1EU9Vb4zZC3z2x3s7qd9129KjoHQt+RHFvbfq1sB68fzaAQtSkxbqSPi6 j8mC0xmK7RGHmjT/+8/npp/Uq60jxMDCtQx10Wi2ceoNXWj1Hx2yXs2cQ9eKM3oUMS HkmfWCcuYUCPg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58EAAC369C9; Wed, 16 Apr 2025 19:21:07 +0000 (UTC) Date: Wed, 16 Apr 2025 19:20:37 +0000 Subject: [PATCH RFC 3/3] inline_encoder: Introduce inline encoder to emit BTF.inline Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250416-btf_inline-v1-3-e4bd2f8adae5@meta.com> References: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> In-Reply-To: <20250416-btf_inline-v1-0-e4bd2f8adae5@meta.com> To: dwarves@vger.kernel.org, bpf@vger.kernel.org Cc: Thierry Treyer , 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 X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1744831265; l=21444; i=ttreyer@meta.com; s=20250416; h=from:subject:message-id; bh=bQZ1Uu9oEGYCvyBAQGNfgW4XnY070QWGPv68903WOC0=; b=cpOhNrdXfSA79SeduoizhqKMJAgX1V2w2hWkuPWDqn6JHnpRcNageZZZ+iOL5+f6f/fLkirHS 8jRAT4IldgxAsRTr6tbH53+3RRL19ibGP6IQLfoXpobFpmT0lSoralS X-Developer-Key: i=ttreyer@meta.com; a=ed25519; pk=2NAyAkZ6zhou7+5zqr5ikv3g5BfFbkznGzvzfKv1nbU= X-Endpoint-Received: by B4 Relay for ttreyer@meta.com/20250416 with auth_id=382 X-Original-From: Thierry Treyer Reply-To: ttreyer@meta.com From: Thierry Treyer X-Patchwork-State: RFC From: Thierry Treyer Signed-off-by: Thierry Treyer --- 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 $) 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, 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, B>[param_count] param_offsets; +}; + +type II_Header = struct { + uint<16> magic == 0xeb9f; + uint<8> version; + uint<8> flags; + uint<32> header_size; + offset, B> inline_info_offset; + offset, B> inline_info_size; + offset, B> location_offset; + offset, 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 + Copyright (C) Red Hat Inc + */ +#include + +#include "dutil.h" +#include "dwarves.h" +#include "inline_encoder.h" +#include "list.h" + +#include +#include +#include +#include +#include +#include +#include + +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 + */ +#include + +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);