From patchwork Tue Nov 8 15:31:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13036463 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DA81FC433FE for ; Tue, 8 Nov 2022 15:32:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233627AbiKHPcE (ORCPT ); Tue, 8 Nov 2022 10:32:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54160 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231740AbiKHPcE (ORCPT ); Tue, 8 Nov 2022 10:32:04 -0500 Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4657BDF4E for ; Tue, 8 Nov 2022 07:32:02 -0800 (PST) Received: by mail-wr1-x42b.google.com with SMTP id k8so21595473wrh.1 for ; Tue, 08 Nov 2022 07:32:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jT/LgpzUWfslmQLEkAk6w3HDvORHoZAt5YV1DAHShCQ=; b=Nxy+9/KxDe6gFxRauQYLlZ8GTbO2L1fkjJ6CniRQ8jejztgHQdr5VDxLm2KNh7uvik pT2n2N5jUQmbiNuP3/Ed6l0SZFMlUnF4cPokbNtknmg2bUZKNmCY2RJTcQLOJkpAgKzA yKhiVn2uf9GqLTS8sC8VbH5kyFJXIv6OzKE2s5zsMLl9MkBZGW4gKnjWtNvSYFtSDD1Z xz1tVbWX4AQSVPtYMHhw1/LxDrW5hpHg1N9u95KVWxbW8QQ9iN7WddqoIe6Ji3NVE+zf rqhSCExBtGU+zFL7LEN9f6wcYhn71Xu/CZATfw5eK7DCiA1Z9lQMD8EPfSjIos7pOm7o aLSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jT/LgpzUWfslmQLEkAk6w3HDvORHoZAt5YV1DAHShCQ=; b=OvYg+BLtGRTFGmIfzm/N2ECWcMur0+igiYHyeaiz5eMyWYpIzfexpsCwPPRGb9vjbl jg+S2q5+WDbKYb+e1ln8XECFIndWD7CfltEb7P4CntWsLv7BKN3GR6EL9KY73uHscjiv uPccRywX1MA8PCyBSae5SZgzVMgeehiOKnILZnFHgdBHGoM2Zgcp0gwXhFbvkYv5OlR2 Wsa0LQ6xWAt4PPZhmqL1RjexlSwXl0/ewWIECdcv21WxZBFlYnW0RSMtYpoHmgOgKRWG 5im/t6u9pJbN/LUlqqg2u9TigId3dlRfME1Pf6P+lBfsDAqNdMzBxuVoXcaNROQU/tmA 1kYQ== X-Gm-Message-State: ACrzQf2Vu15WYAHAgKjgwrvvAZ5M7vhFiD37Zq67wH5wxXWMXvI6WPOw UMXYO8e0FIKMFrS2lm3UtgKx1fGL1I0WwWbe X-Google-Smtp-Source: AMsMyM77LajTjsFksQ2gHXhjkqqLWVa/4dKAyVnzxbK9KCQ7yxBVf2Fs65gOYEVvMeLCiCaQ16YxEw== X-Received: by 2002:adf:ffc2:0:b0:236:61e8:de52 with SMTP id x2-20020adfffc2000000b0023661e8de52mr36097683wrs.59.1667921520341; Tue, 08 Nov 2022 07:32:00 -0800 (PST) Received: from pluto.. (boundsly.muster.volia.net. [93.72.16.93]) by smtp.gmail.com with ESMTPSA id e5-20020adfef05000000b00225307f43fbsm10666822wro.44.2022.11.08.07.31.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Nov 2022 07:31:59 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [PATCH bpf-next v2 1/3] libbpf: __attribute__((btf_decl_tag("..."))) for btf dump in C format Date: Tue, 8 Nov 2022 17:31:33 +0200 Message-Id: <20221108153135.491383-2-eddyz87@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221108153135.491383-1-eddyz87@gmail.com> References: <20221108153135.491383-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Clang's `__attribute__((btf_decl_tag("...")))` is represented in BTF as a record of kind BTF_KIND_DECL_TAG with `type` field pointing to the type annotated with this attribute. This commit adds reconstitution of such attributes for BTF dump in C format. BTF doc says that BTF_KIND_DECL_TAGs should follow a target type but this is not enforced and tests don't honor this restriction. This commit uses hashmap to map types to the list of decl tags. The hashmap is filled by `btf_dump_assign_decl_tags` function called from `btf_dump__new`. It is assumed that total number of types annotated with decl tags is relatively small, thus some space is saved by using hashmap instead of adding a new field to `struct btf_dump_type_aux_state`. It is assumed that the list of decl tags associated with a single type is small. Thus the list is represented by an array which grows linearly. To accommodate older Clang versions decl tags are dumped using the following macro: #if __has_attribute(btf_decl_tag) #define __btf_decl_tag(x) __attribute__((btf_decl_tag(x))) #else #define __btf_decl_tag(x) #endif The macro definition is emitted upon first call to `btf_dump__dump_type`. Clang allows to attach btf_decl_tag attributes to the following kinds of items: - struct/union supported - struct/union field supported - typedef supported - global variables supported - function prototype parameters supported - function not applicable - function parameter not applicable - local variables not applicable Signed-off-by: Eduard Zingerman --- tools/lib/bpf/btf_dump.c | 186 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 8 deletions(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index bf0cc0e986dd..a87132b5fc33 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -75,6 +75,14 @@ struct btf_dump_data { bool is_array_char; }; +/* + * An array of ids of BTF_DECL_TAG objects associated with a specific type. + */ +struct decl_tag_array { + __u32 cnt; + __u32 tag_ids[0]; +}; + struct btf_dump { const struct btf *btf; btf_dump_printf_fn_t printf_fn; @@ -111,6 +119,18 @@ struct btf_dump { * name occurrences */ struct hashmap *ident_names; + /* + * maps type id to decl_tag_array, assume that relatively small + * fraction of types has btf_decl_tag's attached + */ + struct hashmap *decl_tags; + /* indicates whether '#define __btf_decl_tag ...' was printed */ + bool btf_decl_tag_is_defined; + /* + * next id to be scanned for decl tags presence, needed to update + * decl_tags map when dump_type calls are interleaved with BTF updates. + */ + __u32 next_decl_tag_scan_id; /* * data for typed display; allocated if needed. */ @@ -127,6 +147,16 @@ static bool str_equal_fn(const void *a, const void *b, void *ctx) return strcmp(a, b) == 0; } +static size_t identity_hash_fn(const void *key, void *ctx) +{ + return (size_t)key; +} + +static bool identity_equal_fn(const void *k1, const void *k2, void *ctx) +{ + return k1 == k2; +} + static const char *btf_name_of(const struct btf_dump *d, __u32 name_off) { return btf__name_by_offset(d->btf, name_off); @@ -143,6 +173,7 @@ static void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...) static int btf_dump_mark_referenced(struct btf_dump *d); static int btf_dump_resize(struct btf_dump *d); +static int btf_dump_assign_decl_tags(struct btf_dump *d); struct btf_dump *btf_dump__new(const struct btf *btf, btf_dump_printf_fn_t printf_fn, @@ -170,20 +201,28 @@ struct btf_dump *btf_dump__new(const struct btf *btf, d->type_names = hashmap__new(str_hash_fn, str_equal_fn, NULL); if (IS_ERR(d->type_names)) { err = PTR_ERR(d->type_names); - d->type_names = NULL; goto err; } d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL); if (IS_ERR(d->ident_names)) { err = PTR_ERR(d->ident_names); - d->ident_names = NULL; goto err; } + d->decl_tags = hashmap__new(identity_hash_fn, identity_equal_fn, NULL); + if (IS_ERR(d->decl_tags)) { + err = PTR_ERR(d->decl_tags); + goto err; + } + d->next_decl_tag_scan_id = 1; err = btf_dump_resize(d); if (err) goto err; + err = btf_dump_assign_decl_tags(d); + if (err) + goto err; + return d; err: btf_dump__free(d); @@ -219,13 +258,20 @@ static int btf_dump_resize(struct btf_dump *d) return 0; } -static void btf_dump_free_names(struct hashmap *map) +static void btf_dump_free_strs_map(struct hashmap *map, bool free_keys, bool free_values) { size_t bkt; struct hashmap_entry *cur; - hashmap__for_each_entry(map, cur, bkt) - free((void *)cur->key); + if (IS_ERR_OR_NULL(map)) + return; + + hashmap__for_each_entry(map, cur, bkt) { + if (free_keys) + free((void *)cur->key); + if (free_values) + free((void *)cur->value); + } hashmap__free(map); } @@ -248,14 +294,16 @@ void btf_dump__free(struct btf_dump *d) free(d->cached_names); free(d->emit_queue); free(d->decl_stack); - btf_dump_free_names(d->type_names); - btf_dump_free_names(d->ident_names); + btf_dump_free_strs_map(d->type_names, true, false); + btf_dump_free_strs_map(d->ident_names, true, false); + btf_dump_free_strs_map(d->decl_tags, false, true); free(d); } static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr); static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id); +static void btf_dump_ensure_btf_decl_tag_macro(struct btf_dump *d); /* * Dump BTF type in a compilable C syntax, including all the necessary @@ -284,6 +332,9 @@ int btf_dump__dump_type(struct btf_dump *d, __u32 id) if (err) return libbpf_err(err); + btf_dump_ensure_btf_decl_tag_macro(d); + btf_dump_assign_decl_tags(d); + d->emit_queue_cnt = 0; err = btf_dump_order_type(d, id, false); if (err < 0) @@ -373,6 +424,63 @@ static int btf_dump_mark_referenced(struct btf_dump *d) return 0; } +/* + * Scans all BTF objects looking for BTF_KIND_DECL_TAG entries. + * The id's of the entries are stored in the `btf_dump.decl_tags` table, + * grouped by a target type. + */ +static int btf_dump_assign_decl_tags(struct btf_dump *d) +{ + __u32 id, new_cnt, type_cnt = btf__type_cnt(d->btf); + struct decl_tag_array *old_tags, *new_tags; + const struct btf_type *t; + size_t new_sz; + int err; + + for (id = d->next_decl_tag_scan_id; id < type_cnt; id++) { + t = btf__type_by_id(d->btf, id); + if (!btf_is_decl_tag(t)) + continue; + + old_tags = NULL; + hashmap__find(d->decl_tags, (void *)(uintptr_t)t->type, (void **)&old_tags); + /* Assume small number of decl tags per id, increase array size by 1 */ + new_cnt = old_tags ? old_tags->cnt + 1 : 1; + if (new_cnt == UINT_MAX) + return -ERANGE; + new_sz = sizeof(struct decl_tag_array) + new_cnt * sizeof(old_tags->tag_ids[0]); + new_tags = realloc(old_tags, new_sz); + if (!new_tags) + return -ENOMEM; + + new_tags->tag_ids[new_cnt - 1] = id; + new_tags->cnt = new_cnt; + + /* No need to update the map if realloc have not changed the pointer */ + if (old_tags == new_tags) + continue; + + err = hashmap__set(d->decl_tags, (void *)(uintptr_t)t->type, new_tags, + NULL, NULL); + if (!err) + continue; + /* + * If old_tags != NULL there is a record that holds it in the map, thus + * the hashmap__set() call should not fail as it does not have to + * allocate. If it does fail for some bizarre reason it's a bug and double + * free is imminent because of the previous realloc call. + */ + if (old_tags) + pr_warn("hashmap__set() failed to update value for existing entry\n"); + free(new_tags); + return err; + } + + d->next_decl_tag_scan_id = type_cnt; + + return 0; +} + static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) { __u32 *new_queue; @@ -899,6 +1007,51 @@ static void btf_dump_emit_bit_padding(const struct btf_dump *d, } } +/* + * Define __btf_decl_tag to be either __attribute__ or noop. + */ +static void btf_dump_ensure_btf_decl_tag_macro(struct btf_dump *d) +{ + if (d->btf_decl_tag_is_defined || !hashmap__size(d->decl_tags)) + return; + + d->btf_decl_tag_is_defined = true; + btf_dump_printf(d, "#if __has_attribute(btf_decl_tag)\n"); + btf_dump_printf(d, "#define __btf_decl_tag(x) __attribute__((btf_decl_tag(x)))\n"); + btf_dump_printf(d, "#else\n"); + btf_dump_printf(d, "#define __btf_decl_tag(x)\n"); + btf_dump_printf(d, "#endif\n\n"); +} + +/* + * Emits a list of __btf_decl_tag(...) attributes attached to some type. + * Decl tags attached to a type and to it's fields reside in a same + * list, thus use component_idx to filter out relevant tags: + * - component_idx == -1 designates the type itself; + * - component_idx >= 0 designates specific field. + */ +static void btf_dump_emit_decl_tags(struct btf_dump *d, + struct decl_tag_array *decl_tags, + int component_idx) +{ + struct btf_type *t; + const char *text; + struct btf_decl_tag *tag; + __u32 i; + + if (!decl_tags) + return; + + for (i = 0; i < decl_tags->cnt; ++i) { + t = btf_type_by_id(d->btf, decl_tags->tag_ids[i]); + tag = btf_decl_tag(t); + if (tag->component_idx != component_idx) + continue; + text = btf__name_by_offset(d->btf, t->name_off); + btf_dump_printf(d, " __btf_decl_tag(\"%s\")", text); + } +} + static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id, const struct btf_type *t) { @@ -914,11 +1067,13 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, int lvl) { const struct btf_member *m = btf_members(t); + struct decl_tag_array *decl_tags = NULL; bool is_struct = btf_is_struct(t); int align, i, packed, off = 0; __u16 vlen = btf_vlen(t); packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0; + hashmap__find(d->decl_tags, (void *)(uintptr_t)id, (void **)&decl_tags); btf_dump_printf(d, "%s%s%s {", is_struct ? "struct" : "union", @@ -945,6 +1100,7 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, m_sz = max((__s64)0, btf__resolve_size(d->btf, m->type)); off = m_off + m_sz * 8; } + btf_dump_emit_decl_tags(d, decl_tags, i); btf_dump_printf(d, ";"); } @@ -964,6 +1120,7 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, btf_dump_printf(d, "%s}", pfx(lvl)); if (packed) btf_dump_printf(d, " __attribute__((packed))"); + btf_dump_emit_decl_tags(d, decl_tags, -1); } static const char *missing_base_types[][2] = { @@ -1090,6 +1247,7 @@ static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id, const struct btf_type *t, int lvl) { const char *name = btf_dump_ident_name(d, id); + struct decl_tag_array *decl_tags = NULL; /* * Old GCC versions are emitting invalid typedef for __gnuc_va_list @@ -1104,6 +1262,8 @@ static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id, btf_dump_printf(d, "typedef "); btf_dump_emit_type_decl(d, t->type, name, lvl); + hashmap__find(d->decl_tags, (void *)(uintptr_t)id, (void **)&decl_tags); + btf_dump_emit_decl_tags(d, decl_tags, -1); } static int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id) @@ -1438,9 +1598,12 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, } case BTF_KIND_FUNC_PROTO: { const struct btf_param *p = btf_params(t); + struct decl_tag_array *decl_tags = NULL; __u16 vlen = btf_vlen(t); int i; + hashmap__find(d->decl_tags, (void *)(uintptr_t)id, (void **)&decl_tags); + /* * GCC emits extra volatile qualifier for * __attribute__((noreturn)) function pointers. Clang @@ -1481,6 +1644,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, name = btf_name_of(d, p->name_off); btf_dump_emit_type_decl(d, p->type, name, lvl); + btf_dump_emit_decl_tags(d, decl_tags, i); } btf_dump_printf(d, ")"); @@ -1897,6 +2061,7 @@ static int btf_dump_var_data(struct btf_dump *d, const void *data) { enum btf_func_linkage linkage = btf_var(v)->linkage; + struct decl_tag_array *decl_tags = NULL; const struct btf_type *t; const char *l; __u32 type_id; @@ -1921,7 +2086,10 @@ static int btf_dump_var_data(struct btf_dump *d, type_id = v->type; t = btf__type_by_id(d->btf, type_id); btf_dump_emit_type_cast(d, type_id, false); - btf_dump_printf(d, " %s = ", btf_name_of(d, v->name_off)); + btf_dump_printf(d, " %s", btf_name_of(d, v->name_off)); + hashmap__find(d->decl_tags, (void *)(uintptr_t)id, (void **)&decl_tags); + btf_dump_emit_decl_tags(d, decl_tags, -1); + btf_dump_printf(d, " = "); return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0); } @@ -2422,6 +2590,8 @@ int btf_dump__dump_type_data(struct btf_dump *d, __u32 id, d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false); d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false); + btf_dump_assign_decl_tags(d); + ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0); d->typed_dump = NULL; From patchwork Tue Nov 8 15:31:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13036464 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D14D8C43217 for ; Tue, 8 Nov 2022 15:32:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231740AbiKHPcF (ORCPT ); Tue, 8 Nov 2022 10:32:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232923AbiKHPcE (ORCPT ); Tue, 8 Nov 2022 10:32:04 -0500 Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 610A7EE0F for ; Tue, 8 Nov 2022 07:32:03 -0800 (PST) Received: by mail-wr1-x431.google.com with SMTP id bk15so21498615wrb.13 for ; Tue, 08 Nov 2022 07:32:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SfhLrjMmd1glswz5hr/esjMdBgxEvM9G/4scmePJDNc=; b=PRxFbuMadMvH8r/TCExkaI5fZZDONnkabGBQyI3TYyZK4uTKVUPoMNkXwZrspSYpGs 0EyHQRxFkbMqrYW0YxJvHPimwmzaC0KY4HLeifCV/iiAxkDWjHMfPHDHslNMs2LaN1DE Lo5hy91OFr4ckhIBnYU4L+Um0FfhHpoDqZotThUpbCpe7AzLFZhZtz8CY/7LuTcN3y71 8MddmonuBN1k2+cCVmlNOyEmTv+MXBF21mPj2/99kjKFMBDrCe6/7lxk/hmE5/gZoCfA wplMe9PKonloyAH6wYzMna4puN1/LNStThfadI1UV0BWuFr+NC3yrOdrtKN96Z2fgGxV 1ObQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SfhLrjMmd1glswz5hr/esjMdBgxEvM9G/4scmePJDNc=; b=aHqZqCpPLf2ihI79+khbtm19bnno7fjZEk/fksizeBe9V2SghlzevtsvLUZip2mP31 CLI3gxjz9jqKinlh4HZCGcp3Kx/e32kIaBjEBHuWF1tXNLr14iVyP2ll8FQPVhx9JnR1 dbMQI/5ryHmvDl4NhkHXF9aX35NMsM1i8vl51jvPXNc0uM9wcUHg8rAFBHIqg0Yt8PIu KaKbIX5rhxqxV12s4llCNiM/xJrDin5uwHdi1dgshSy3hV7dFQwxrIXrSsco70A6/xSm th3NjPkhIO7e2Q7i6yy01HXeXEDraFbasfU+3k50BSTVQ0/tteF1P524VQMaSFXFwZys VIsg== X-Gm-Message-State: ACrzQf3Qr+iKqNcYbUQiB2lthlWBI9NniZJE7HFZReGlbBETPz5gHR0h 7lhGvBPf3SWw0cBp6l437B8lyp86IxfhK2UB X-Google-Smtp-Source: AMsMyM7nq4on2pSOamrxaK1GEVEVw3n7Ra4f6lI+uWMVcCXUYWJjFa4tpkWeitYx46NYqe3KpLJh3Q== X-Received: by 2002:a5d:480d:0:b0:236:5817:2299 with SMTP id l13-20020a5d480d000000b0023658172299mr36845879wrq.371.1667921521702; Tue, 08 Nov 2022 07:32:01 -0800 (PST) Received: from pluto.. (boundsly.muster.volia.net. [93.72.16.93]) by smtp.gmail.com with ESMTPSA id e5-20020adfef05000000b00225307f43fbsm10666822wro.44.2022.11.08.07.32.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Nov 2022 07:32:01 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [PATCH bpf-next v2 2/3] selftests/bpf: Dump data sections as part of btf_dump_test_case tests Date: Tue, 8 Nov 2022 17:31:34 +0200 Message-Id: <20221108153135.491383-3-eddyz87@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221108153135.491383-1-eddyz87@gmail.com> References: <20221108153135.491383-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Modify `test_btf_dump_case` to test `btf_dump__dump_type_data` alongside `btf_dump__dump_type`. The `test_btf_dump_case` function provides a convenient way to test `btf_dump__dump_type` behavior as test cases are specified in separate C files and any differences are reported using `diff` utility. This commit extends `test_btf_dump_case` to call `btf_dump__dump_type_data` for each `BTF_KIND_DATASEC` object in the test case object file. Signed-off-by: Eduard Zingerman --- .../selftests/bpf/prog_tests/btf_dump.c | 118 +++++++++++++++--- 1 file changed, 104 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index 24da335482d4..a0bdfc45660d 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include +#include static int duration = 0; @@ -23,31 +25,104 @@ static struct btf_dump_test_case { {"btf_dump: namespacing", "btf_dump_test_case_namespacing", false}, }; -static int btf_dump_all_types(const struct btf *btf, void *ctx) +static int btf_dump_all_types(const struct btf *btf, struct btf_dump *d) { size_t type_cnt = btf__type_cnt(btf); - struct btf_dump *d; int err = 0, id; - d = btf_dump__new(btf, btf_dump_printf, ctx, NULL); - err = libbpf_get_error(d); - if (err) - return err; - for (id = 1; id < type_cnt; id++) { err = btf_dump__dump_type(d, id); if (err) - goto done; + break; + } + + return err; +} + +/* Keep this as macro to retain __FILE__, __LINE__ values used by PRINT_FAIL */ +#define report_elf_error(fn) \ + ({ \ + int __err = elf_errno(); \ + PRINT_FAIL("%s() failed %s(%d)\n", fn, elf_errmsg(__err), __err); \ + __err; \ + }) + +static int btf_dump_datasec(Elf *elf, const struct btf *btf, struct btf_dump *d, __u32 id) +{ + const char *btf_sec, *elf_sec; + const struct btf_type *t; + Elf_Data *data = NULL; + Elf_Scn *scn = NULL; + size_t shstrndx; + GElf_Shdr sh; + + if (elf_getshdrstrndx(elf, &shstrndx)) + return report_elf_error("elf_getshdrstrndx"); + + t = btf__type_by_id(btf, id); + btf_sec = btf__str_by_offset(btf, t->name_off); + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (!gelf_getshdr(scn, &sh)) + return report_elf_error("gelf_getshdr"); + elf_sec = elf_strptr(elf, shstrndx, sh.sh_name); + if (!elf_sec) + return report_elf_error("elf_strptr"); + if (strcmp(btf_sec, elf_sec) == 0) { + data = elf_getdata(scn, NULL); + if (!data) + return report_elf_error("elf_getdata"); + break; + } + } + + if (CHECK(!data, "btf_dump_datasec", "can't find ELF section %s\n", elf_sec)) + return -1; + + return btf_dump__dump_type_data(d, id, data->d_buf, data->d_size, NULL); +} + +static int btf_dump_all_datasec(const struct btf *btf, struct btf_dump *d, + char *test_file, FILE *f) +{ + size_t type_cnt = btf__type_cnt(btf); + int err = 0, id, fd = 0; + Elf *elf = NULL; + + fd = open(test_file, O_RDONLY | O_CLOEXEC); + if (CHECK(fd < 0, "open", "can't open %s for reading, %s(%d)\n", + test_file, strerror(errno), errno)) { + err = errno; + goto done; + } + + elf = elf_begin(fd, ELF_C_READ, NULL); + if (!elf) { + err = report_elf_error("elf_begin"); + goto done; + } + + for (id = 1; id < type_cnt; id++) { + if (!btf_is_datasec(btf__type_by_id(btf, id))) + continue; + err = btf_dump_datasec(elf, btf, d, id); + if (err) + break; + fprintf(f, "\n\n"); } done: - btf_dump__free(d); + if (fd) + close(fd); + if (elf) + elf_end(elf); return err; } static int test_btf_dump_case(int n, struct btf_dump_test_case *t) { char test_file[256], out_file[256], diff_cmd[1024]; + struct btf_dump *d = NULL; struct btf *btf = NULL; int err = 0, fd = -1; FILE *f = NULL; @@ -86,12 +161,22 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t) goto done; } - err = btf_dump_all_types(btf, f); - fclose(f); - close(fd); - if (CHECK(err, "btf_dump", "failure during C dumping: %d\n", err)) { + d = btf_dump__new(btf, btf_dump_printf, f, NULL); + err = libbpf_get_error(d); + if (CHECK(err, "btf_dump", "btf_dump__new failed: %d\n", err)) + goto done; + + err = btf_dump_all_types(btf, d); + if (CHECK(err, "btf_dump", "btf_dump_all_types failed: %d\n", err)) + goto done; + + err = btf_dump_all_datasec(btf, d, test_file, f); + if (CHECK(err, "btf_dump", "btf_dump_all_datasec failed: %d\n", err)) + goto done; + + if (CHECK(fflush(f), "btf_dump", "fflush() on %s failed: %s(%d)\n", + test_file, strerror(errno), errno)) goto done; - } snprintf(test_file, sizeof(test_file), "progs/%s.c", t->file); if (access(test_file, R_OK) == -1) @@ -122,6 +207,11 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t) remove(out_file); done: + if (f) + fclose(f); + if (fd >= 0) + close(fd); + btf_dump__free(d); btf__free(btf); return err; } From patchwork Tue Nov 8 15:31:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13036465 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5EA85C43219 for ; Tue, 8 Nov 2022 15:32:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233751AbiKHPcH (ORCPT ); Tue, 8 Nov 2022 10:32:07 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232923AbiKHPcG (ORCPT ); Tue, 8 Nov 2022 10:32:06 -0500 Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 871ABDF4E for ; Tue, 8 Nov 2022 07:32:04 -0800 (PST) Received: by mail-wr1-x42a.google.com with SMTP id o4so21574239wrq.6 for ; Tue, 08 Nov 2022 07:32:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jSq5pSSmv6HFFK0BYaZp6IdaOMj0pgqR16ivoycbc3k=; b=VSJwvKdpk23iPCF9Vg14U3cLvNDLjEvwWOtE/YQm1cgN5uxoCyVd2f70llQKApvM+W cPW/RjfIauBc1mBvCxu4Yzqsz7wKm6CKPWw+620To7kj922ztBOehx/p5jklRIxx8WcU UrUYL0q1WupGooZ9Y923T3eOmKE//nvVkTTC5H4zib9d8uvyl2eRsAXhQtNA+Ziur552 Zd5T7FILyjoDLpiPjZn3OflvBQUWDixd9EzPRLIY329glXBRc922kCs1Lqi0CU2F2C9a 7vRZc8Q1dPBDMjpnlCvnzYJGaccwcG89NIRlSmh5TDCJQilXiRIYeRmtjjxL4FceEAW5 cUyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jSq5pSSmv6HFFK0BYaZp6IdaOMj0pgqR16ivoycbc3k=; b=JjvQ7sZr6yGwpAQXjBvHV6QeUfzU1V9XiFX4W1Je29UNLDRVTDXrus/Jy5QL46H9Y7 zpdF2mUAzEfqinI/yJUFgKR5ZF95eJGxHyZevD4d3h5As1tw51OKpCPIH/UK4PpBFlhS p2nTgWZyfEBNhuYzd/nHd8pDef6s/j8N7KaZNiTWdu/ZlgugMpWpUifkT7+cn0mwWDks LBbNbTBlv3KREYyT8A5AHVLD3/bJB28CAslYboWnNHfBPMwYBdR0AiYw7bJbJNGphv0Q 5+uMtHaqK+NN1vmQjAcVxkee4pfvUf2Kqhs8bQTbQg7tEtz1/eMgyZb2rs+OrK1lUmgS 5oew== X-Gm-Message-State: ACrzQf2klEu3hdK/4X6cpyjjg1DS0qBxJGIEdZW1+crXpO3Km6hHLQM9 pJq0gTXeceAi6B3FF6fOSCRlfBHB0tuDyj7u X-Google-Smtp-Source: AMsMyM7QP19NbDA8rZqeNXvCnYXJKt6za38x+cwWOeA3g+rXjgRVb1qau+ID1Re7DNtXUDRYME2PPg== X-Received: by 2002:a05:6000:144d:b0:236:f53e:363d with SMTP id v13-20020a056000144d00b00236f53e363dmr21253685wrx.422.1667921522793; Tue, 08 Nov 2022 07:32:02 -0800 (PST) Received: from pluto.. (boundsly.muster.volia.net. [93.72.16.93]) by smtp.gmail.com with ESMTPSA id e5-20020adfef05000000b00225307f43fbsm10666822wro.44.2022.11.08.07.32.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Nov 2022 07:32:02 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com, yhs@fb.com, Eduard Zingerman Subject: [PATCH bpf-next v2 3/3] selftests/bpf: Tests for BTF_KIND_DECL_TAG dump in C format Date: Tue, 8 Nov 2022 17:31:35 +0200 Message-Id: <20221108153135.491383-4-eddyz87@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221108153135.491383-1-eddyz87@gmail.com> References: <20221108153135.491383-1-eddyz87@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Covers the following cases: - `__atribute__((btf_decl_tag("...")))` could be applied to structs and unions; - decl tag applied to an empty struct is printed on a single line; - decl tags with the same name could be applied to several structs; - several decl tags could be applied to the same struct; - attribute `packed` works fine with decl tags (it is a separate branch in `tools/lib/bpf/btf_dump.c:btf_dump_emit_attributes`; - decl tag could be attached to typedef; - decl tag could be attached to a struct field; - decl tag could be attached to a struct field and a struct itself simultaneously; - decl tag could be attached to a global variable; - decl tag could be attached to a func proto parameter; - btf__add_decl_tag could be interleaved with btf_dump__dump_type calls. Signed-off-by: Eduard Zingerman --- .../selftests/bpf/prog_tests/btf_dump.c | 80 +++++++++++++++++++ .../bpf/progs/btf_dump_test_case_decl_tag.c | 65 +++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf_dump_test_case_decl_tag.c diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index a0bdfc45660d..0d428c559ad9 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -23,6 +23,7 @@ static struct btf_dump_test_case { {"btf_dump: bitfields", "btf_dump_test_case_bitfields", true}, {"btf_dump: multidim", "btf_dump_test_case_multidim", false}, {"btf_dump: namespacing", "btf_dump_test_case_namespacing", false}, + {"btf_dump: decl_tag", "btf_dump_test_case_decl_tag", true}, }; static int btf_dump_all_types(const struct btf *btf, struct btf_dump *d) @@ -345,6 +346,82 @@ static void test_btf_dump_incremental(void) btf__free(btf); } +static void test_btf_dump_func_proto_decl_tag(void) +{ + struct btf *btf = NULL; + struct btf_dump *d = NULL; + int id, err; + + dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz); + if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream")) + return; + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "new_empty")) + goto err_out; + d = btf_dump__new(btf, btf_dump_printf, dump_buf_file, NULL); + if (!ASSERT_OK(libbpf_get_error(d), "btf_dump__new")) + goto err_out; + + /* First, BTF corresponding to the following C code: + * + * typedef void (*fn)(int a __btf_decl_tag("a_tag")); + * + */ + id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); + ASSERT_EQ(id, 1, "int_id"); + id = btf__add_func_proto(btf, 0); + ASSERT_EQ(id, 2, "func_proto_id"); + err = btf__add_func_param(btf, "a", 1); + ASSERT_OK(err, "func_param_ok"); + id = btf__add_decl_tag(btf, "a_tag", 2, 0); + ASSERT_EQ(id, 3, "decl_tag_a"); + id = btf__add_ptr(btf, 2); + ASSERT_EQ(id, 4, "proto_ptr"); + id = btf__add_typedef(btf, "fn", 4); + ASSERT_EQ(id, 5, "typedef"); + + err = btf_dump_all_types(btf, d); + ASSERT_OK(err, "btf_dump_all_types #1"); + fflush(dump_buf_file); + dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ + + ASSERT_STREQ(dump_buf, + "#if __has_attribute(btf_decl_tag)\n" + "#define __btf_decl_tag(x) __attribute__((btf_decl_tag(x)))\n" + "#else\n" + "#define __btf_decl_tag(x)\n" + "#endif\n" + "\n" + "typedef void (*fn)(int a __btf_decl_tag(\"a_tag\"));\n\n", + "decl tags for fn"); + + /* Next, add BTF corresponding to the following C code: + * + * typedef int foo __btf_decl_tag("foo_tag"); + * + * To verify that decl_tag's table is updated incrementally. + */ + id = btf__add_typedef(btf, "foo", 1); + ASSERT_EQ(id, 6, "typedef"); + id = btf__add_decl_tag(btf, "foo_tag", 6, -1); + ASSERT_EQ(id, 7, "decl_tag_foo"); + + fseek(dump_buf_file, 0, SEEK_SET); + err = btf_dump_all_types(btf, d); + ASSERT_OK(err, "btf_dump_all_types #2"); + fflush(dump_buf_file); + dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ + + ASSERT_STREQ(dump_buf, + "typedef int foo __btf_decl_tag(\"foo_tag\");\n\n", + "decl tags for foo"); +err_out: + fclose(dump_buf_file); + free(dump_buf); + btf_dump__free(d); + btf__free(btf); +} + #define STRSIZE 4096 static void btf_dump_snprintf(void *ctx, const char *fmt, va_list args) @@ -964,6 +1041,9 @@ void test_btf_dump() { if (test__start_subtest("btf_dump: incremental")) test_btf_dump_incremental(); + if (test__start_subtest("btf_dump: func arg decl_tag")) + test_btf_dump_func_proto_decl_tag(); + btf = libbpf_find_kernel_btf(); if (!ASSERT_OK_PTR(btf, "no kernel BTF found")) return; diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_decl_tag.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_decl_tag.c new file mode 100644 index 000000000000..1205351e9a68 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_decl_tag.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +/* + * BTF-to-C dumper test for __atribute__((btf_decl_tag("..."))). + */ + +#define SEC(x) __attribute__((section(x))) + +/* ----- START-EXPECTED-OUTPUT ----- */ +#if __has_attribute(btf_decl_tag) +#define __btf_decl_tag(x) __attribute__((btf_decl_tag(x))) +#else +#define __btf_decl_tag(x) +#endif + +struct empty_with_tag {} __btf_decl_tag("a"); + +struct one_tag { + int x; +} __btf_decl_tag("b"); + +struct same_tag { + int x; +} __btf_decl_tag("b"); + +struct two_tags { + int x; +} __btf_decl_tag("a") __btf_decl_tag("b"); + +struct packed { + int x; + short y; +} __attribute__((packed)) __btf_decl_tag("another_name"); + +typedef int td_with_tag __btf_decl_tag("td"); + +struct tags_on_fields { + int x __btf_decl_tag("t1"); + int y; + int z __btf_decl_tag("t2") __btf_decl_tag("t3"); +}; + +struct tag_on_field_and_struct { + int x __btf_decl_tag("t1"); +} __btf_decl_tag("t2"); + +struct root_struct { + struct empty_with_tag a; + struct one_tag b; + struct same_tag c; + struct two_tags d; + struct packed e; + td_with_tag f; + struct tags_on_fields g; + struct tag_on_field_and_struct h; +}; + +SEC(".data") int global_var __btf_decl_tag("var_tag") = (int)777; + +/* ------ END-EXPECTED-OUTPUT ------ */ + +int f(struct root_struct *s) +{ + return 0; +}