From patchwork Sun Jan 17 22:16:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 12025865 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 X-Spam-Level: X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D4A4C433DB for ; Sun, 17 Jan 2021 22:26:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4828A2247F for ; Sun, 17 Jan 2021 22:26:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730484AbhAQW0N (ORCPT ); Sun, 17 Jan 2021 17:26:13 -0500 Received: from userp2130.oracle.com ([156.151.31.86]:33016 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730277AbhAQWXa (ORCPT ); Sun, 17 Jan 2021 17:23:30 -0500 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMMXWl189253; Sun, 17 Jan 2021 22:22:33 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=g6eUC0AZE2t/y3Z9jca9TL3vndYBoHLh6eAFxSYY+RE=; b=D1Z2nUzCOsjRjzmhGUSC9X7Y4hTV0nuSuYwaog2hZtfHNue5fuR5LW/mUWc84gQGWkvw mdBE/Pi3ORPKAms6bTkqbKrSkp0ZfcPDGXWeunIEdU4MrGo8N3n56TZb0ZTts8y+rQ7F 0ombTomkM2hcO+6cayTlNufjm/YaL9wYOFkiODZFGZ8wKnnaQrHPY7X208sSX7NZAoCh l/nZyyD293/FQSMe0ksXkT1JTpZmhVPLg5lO0+zQDYKRZHVM5Q3Xb4BRMJBlQG6WsIt1 parmaH68NMyf2rKqWHYtZ1pDH9OAZ+jsAM9f/bb76iO2Z+7d6dj3wSFwii2BidgV1uMM uA== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2130.oracle.com with ESMTP id 363xyhj8g2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:22:33 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMBJRt092429; Sun, 17 Jan 2021 22:20:32 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by userp3030.oracle.com with ESMTP id 364a2u5ytd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:32 +0000 Received: from abhmp0003.oracle.com (abhmp0003.oracle.com [141.146.116.9]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id 10HMKVFb011654; Sun, 17 Jan 2021 22:20:31 GMT Received: from localhost.localdomain (/95.45.14.174) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 17 Jan 2021 14:20:30 -0800 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org Cc: kafai@fb.com, songliubraving@fb.com, yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org, morbo@google.com, shuah@kernel.org, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Maguire Subject: [PATCH v2 bpf-next 1/4] libbpf: add btf_has_size() and btf_int() inlines Date: Sun, 17 Jan 2021 22:16:01 +0000 Message-Id: <1610921764-7526-2-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> References: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 mlxscore=0 suspectscore=0 phishscore=0 mlxlogscore=999 bulkscore=0 adultscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170139 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 adultscore=0 phishscore=0 malwarescore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 spamscore=0 mlxscore=0 impostorscore=0 lowpriorityscore=0 suspectscore=0 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170140 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net BTF type data dumping will use them in later patches, and they are useful generally when handling BTF data. Signed-off-by: Alan Maguire --- tools/lib/bpf/btf.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 1237bcd..0c48f2e 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -294,6 +294,20 @@ static inline bool btf_is_datasec(const struct btf_type *t) return btf_kind(t) == BTF_KIND_DATASEC; } +static inline bool btf_has_size(const struct btf_type *t) +{ + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_INT: + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + case BTF_KIND_ENUM: + case BTF_KIND_DATASEC: + return true; + default: + return false; + } +} + static inline __u8 btf_int_encoding(const struct btf_type *t) { return BTF_INT_ENCODING(*(__u32 *)(t + 1)); @@ -309,6 +323,11 @@ static inline __u8 btf_int_bits(const struct btf_type *t) return BTF_INT_BITS(*(__u32 *)(t + 1)); } +static inline __u32 btf_int(const struct btf_type *t) +{ + return *(__u32 *)(t + 1); +} + static inline struct btf_array *btf_array(const struct btf_type *t) { return (struct btf_array *)(t + 1); From patchwork Sun Jan 17 22:16:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 12025859 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 X-Spam-Level: X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2A07BC433DB for ; Sun, 17 Jan 2021 22:23:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EED1D224B0 for ; Sun, 17 Jan 2021 22:23:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730336AbhAQWVk (ORCPT ); Sun, 17 Jan 2021 17:21:40 -0500 Received: from aserp2120.oracle.com ([141.146.126.78]:54944 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730253AbhAQWVf (ORCPT ); Sun, 17 Jan 2021 17:21:35 -0500 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMKbVc075239; Sun, 17 Jan 2021 22:20:37 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=bRmwOY57xXtZW86mDxjYm8OH2EnZFkmiALdzHfMFHjI=; b=KGmfN8luvrVnLnk3tD8o13tEX7oW3oqP4jV2VsJINhiWz31/KoxQXi3cbJzvSfTItE8o bgLOULuili2BI1F1zHuCubWQ0jtU+pUw7H92vndudrqF2s0k5gMpPlhvfagHAlhkJGFj KmJMPg45W/c4qPVT6fc4dTQFebdZEUbPDx05n970/zaNpkyAx+d1pFUpA8VGjnvrTW6m ozXFvOHy04obYPuGLteBK3zTBUoHsh+8vBDSIy7zMxpTzWkFO6TECHzgvfYEQRCxaqOt OaehiTDNFJvEJijwAunjbjm82TKTO39rLFcOG74Uq54Ulok0/v2qAHwyOqHjAeLHCHCn rQ== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by aserp2120.oracle.com with ESMTP id 363r3kjvrf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:37 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMBJIW092448; Sun, 17 Jan 2021 22:20:36 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userp3030.oracle.com with ESMTP id 364a2u5yu6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:36 +0000 Received: from abhmp0003.oracle.com (abhmp0003.oracle.com [141.146.116.9]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 10HMKZ2u014917; Sun, 17 Jan 2021 22:20:35 GMT Received: from localhost.localdomain (/95.45.14.174) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 17 Jan 2021 14:20:34 -0800 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org Cc: kafai@fb.com, songliubraving@fb.com, yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org, morbo@google.com, shuah@kernel.org, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Maguire Subject: [PATCH v2 bpf-next 2/4] libbpf: make skip_mods_and_typedefs available internally in libbpf Date: Sun, 17 Jan 2021 22:16:02 +0000 Message-Id: <1610921764-7526-3-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> References: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 mlxscore=0 suspectscore=0 phishscore=0 mlxlogscore=999 bulkscore=0 adultscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170139 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxlogscore=999 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 priorityscore=1501 mlxscore=0 malwarescore=0 phishscore=0 suspectscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170140 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net btf_dump.c will need it for type-based data display. Signed-off-by: Alan Maguire Acked-by: Andrii Nakryiko --- tools/lib/bpf/libbpf.c | 4 +--- tools/lib/bpf/libbpf_internal.h | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 2abbc38..4ef84e1 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -73,8 +73,6 @@ #define __printf(a, b) __attribute__((format(printf, a, b))) static struct bpf_map *bpf_object__add_map(struct bpf_object *obj); -static const struct btf_type * -skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id); static int __base_pr(enum libbpf_print_level level, const char *format, va_list args) @@ -1885,7 +1883,7 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) return 0; } -static const struct btf_type * +const struct btf_type * skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id) { const struct btf_type *t = btf__type_by_id(btf, id); diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 969d0ac..c25d2df 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -108,6 +108,8 @@ static inline void *libbpf_reallocarray(void *ptr, size_t nmemb, size_t size) void *btf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t cur_cnt, size_t max_cnt, size_t add_cnt); int btf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt); +const struct btf_type *skip_mods_and_typedefs(const struct btf *btf, __u32 id, + __u32 *res_id); static inline bool libbpf_validate_opts(const char *opts, size_t opts_sz, size_t user_sz, From patchwork Sun Jan 17 22:16:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 12025861 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 X-Spam-Level: X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55BA8C433E0 for ; Sun, 17 Jan 2021 22:24:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 18AC9224D2 for ; Sun, 17 Jan 2021 22:24:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730534AbhAQWXz (ORCPT ); Sun, 17 Jan 2021 17:23:55 -0500 Received: from userp2130.oracle.com ([156.151.31.86]:33114 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730507AbhAQWXo (ORCPT ); Sun, 17 Jan 2021 17:23:44 -0500 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMKvwv188383; Sun, 17 Jan 2021 22:22:41 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=oefBKqRlbuFOIjFPyxlyMZVmfjGhuaTtOUyoMW0nk7o=; b=Q3XoIUHpLEpc18tBf+ouGp6draxT/UjeNuXEOskz0Sv/5TOgi6Uz+aaRuAuAQLILRB93 ACrJWt+S1ZXOLZKfEUdjBURZpYgPV9EKZnUTiixsp1hwOZjh9n9THj9SCpRczzW70I7o MUpOVDKBwH1p7UQL6VnW82mexoxtopiFbFNGvcNtrvELQQ4f2niRS6BdvqV6dyDVnJQF 9qRw5YYt1oLWtoKvWw9sGXwZmkmsA+LnusH8ue0m4BBQHGhccNaPz8QXbZx839o+GFi6 DPghoL/uUuCiOIdSAA4G8lBYCyvoRnKo+6f0HBkIrwhvz2smBON+zzrgOp3v+uZt5KEz uw== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2130.oracle.com with ESMTP id 363xyhj8g5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:22:40 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMBJwm092369; Sun, 17 Jan 2021 22:20:40 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userp3030.oracle.com with ESMTP id 364a2u5yur-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:40 +0000 Received: from abhmp0003.oracle.com (abhmp0003.oracle.com [141.146.116.9]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 10HMKcqr014928; Sun, 17 Jan 2021 22:20:39 GMT Received: from localhost.localdomain (/95.45.14.174) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 17 Jan 2021 14:20:38 -0800 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org Cc: kafai@fb.com, songliubraving@fb.com, yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org, morbo@google.com, shuah@kernel.org, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Maguire Subject: [PATCH v2 bpf-next 3/4] libbpf: BTF dumper support for typed data Date: Sun, 17 Jan 2021 22:16:03 +0000 Message-Id: <1610921764-7526-4-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> References: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 mlxscore=0 suspectscore=0 phishscore=0 mlxlogscore=999 bulkscore=0 adultscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170139 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 adultscore=0 phishscore=0 malwarescore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 spamscore=0 mlxscore=0 impostorscore=0 lowpriorityscore=0 suspectscore=0 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170140 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add a BTF dumper for typed data, so that the user can dump a typed version of the data provided. The API is int btf_dump__emit_type_data(struct btf_dump *d, __u32 id, const struct btf_dump_emit_type_data_opts *opts, void *data); ...where the id is the BTF id of the data pointed to by the "void *" argument; for example the BTF id of "struct sk_buff" for a "struct skb *" data pointer. Options supported are - a starting indent level (indent_lvl) - a set of boolean options to control dump display, similar to those used for BPF helper bpf_snprintf_btf(). Options are - compact : omit newlines and other indentation - noname: omit member names - zero: show zero-value members Default output format is identical to that dumped by bpf_snprintf_btf(), for example a "struct sk_buff" representation would look like this: struct sk_buff){ (union){ (struct){ .next = (struct sk_buff *)0xffffffffffffffff, .prev = (struct sk_buff *)0xffffffffffffffff, (union){ .dev = (struct net_device *)0xffffffffffffffff, .dev_scratch = (long unsigned int)18446744073709551615, }, }, ... Signed-off-by: Alan Maguire --- tools/lib/bpf/btf.h | 17 + tools/lib/bpf/btf_dump.c | 974 +++++++++++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.map | 5 + 3 files changed, 996 insertions(+) diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 0c48f2e..7937124 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -180,6 +180,23 @@ struct btf_dump_emit_type_decl_opts { btf_dump__emit_type_decl(struct btf_dump *d, __u32 id, const struct btf_dump_emit_type_decl_opts *opts); + +struct btf_dump_emit_type_data_opts { + /* size of this struct, for forward/backward compatibility */ + size_t sz; + int indent_level; + /* below match "show" flags for bpf_show_snprintf() */ + bool compact; + bool noname; + bool zero; +}; +#define btf_dump_emit_type_data_opts__last_field zero + +LIBBPF_API int +btf_dump__emit_type_data(struct btf_dump *d, __u32 id, + const struct btf_dump_emit_type_data_opts *opts, + void *data); + /* * A set of helpers for easier BTF types handling */ diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 2f9d685..04d604f 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,14 +21,31 @@ #include "libbpf.h" #include "libbpf_internal.h" +#define BITS_PER_BYTE 8 +#define BITS_PER_U128 (sizeof(__u64) * BITS_PER_BYTE * 2) +#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) +#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) +#define BITS_ROUNDUP_BYTES(bits) \ + (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) + static const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; static const size_t PREFIX_CNT = sizeof(PREFIXES) - 1; + static const char *pfx(int lvl) { return lvl >= PREFIX_CNT ? PREFIXES : &PREFIXES[PREFIX_CNT - lvl]; } +static const char SPREFIXES[] = " "; +static const size_t SPREFIX_CNT = sizeof(SPREFIXES) - 1; + +static const char *spfx(int lvl) +{ + return lvl >= SPREFIX_CNT ? SPREFIXES : &SPREFIXES[SPREFIX_CNT - lvl]; +} + enum btf_dump_type_order_state { NOT_ORDERED, ORDERING, @@ -53,6 +72,49 @@ struct btf_dump_type_aux_state { __u8 referenced: 1; }; +#define BTF_DUMP_DATA_MAX_NAME_LEN 256 + +/* + * Common internal data for BTF type data dump operations. + * + * The implementation here is similar to that in kernel/bpf/btf.c + * that supports the bpf_snprintf_btf() helper, so any bugs in + * type data dumping here are likely in that code also. + * + * One challenge with showing nested data is we want to skip 0-valued + * data, but in order to figure out whether a nested object is all zeros + * we need to walk through it. As a result, we need to make two passes + * when handling structs, unions and arrays; the first path simply looks + * for nonzero data, while the second actually does the display. The first + * pass is signalled by state.depth_check being set, and if we + * encounter a non-zero value we set state.depth_to_show to the depth + * at which we encountered it. When we have completed the first pass, + * we will know if anything needs to be displayed if + * state.depth_to_show > state.depth. See btf_dump_emit_[struct,array]_data() + * for the implementation of this. + * + */ +struct btf_dump_data { + bool compact; + bool noname; + bool zero; + __u8 indent_lvl; /* base indent level */ + /* below are used during iteration */ + struct { + __u8 depth; + __u8 depth_to_show; + __u8 depth_check; + __u8 array_member:1, + array_terminated:1; + __u16 array_encoding; + __u32 type_id; + const struct btf_type *type; + const struct btf_member *member; + char name[BTF_DUMP_DATA_MAX_NAME_LEN]; + int err; + } state; +}; + struct btf_dump { const struct btf *btf; const struct btf_ext *btf_ext; @@ -89,6 +151,10 @@ struct btf_dump { * name occurrences */ struct hashmap *ident_names; + /* + * data for typed display. + */ + struct btf_dump_data data; }; static size_t str_hash_fn(const void *key, void *ctx) @@ -1438,3 +1504,911 @@ static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id) { return btf_dump_resolve_name(d, id, d->ident_names); } + +static void __btf_dump_emit_type_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset); + +static const struct btf_type *skip_mods(const struct btf *btf, + __u32 id, __u32 *res_id) +{ + const struct btf_type *t = btf__type_by_id(btf, id); + + while (btf_is_mod(t)) { + id = t->type; + t = btf__type_by_id(btf, t->type); + } + + if (res_id) + *res_id = id; + + return t; +} + +#define BTF_MAX_ITER 10 +#define BTF_KIND_BIT(kind) (1ULL << kind) + +/* + * Populate dump->data.state.name with type name information. + * Format of type name is + * + * [.member_name = ] (type_name) + */ +static const char *btf_dump_data_name(struct btf_dump *d) +{ + /* BTF_MAX_ITER array suffixes "[]" */ + const char *array_suffixes = "[][][][][][][][][][]"; + const char *array_suffix = &array_suffixes[strlen(array_suffixes)]; + /* BTF_MAX_ITER pointer suffixes "*" */ + const char *ptr_suffixes = "**********"; + const char *ptr_suffix = &ptr_suffixes[strlen(ptr_suffixes)]; + const char *name = NULL, *prefix = "", *parens = ""; + const struct btf_member *m = d->data.state.member; + const struct btf_type *t = d->data.state.type; + const struct btf_array *array; + __u32 id = d->data.state.type_id; + const char *member = NULL; + bool show_member = false; + __u64 kinds = 0; + int i; + + d->data.state.name[0] = '\0'; + + /* + * Don't show type name if we're showing an array member; + * in that case we show the array type so don't need to repeat + * ourselves for each member. + */ + if (d->data.state.array_member) + return ""; + + /* Retrieve member name, if any. */ + if (m) { + member = btf_name_of(d, m->name_off); + show_member = strlen(member) > 0; + id = m->type; + } + + /* + * Start with type_id, as we have resolved the struct btf_type * + * via btf_dump_emit_modifier_data() past the parent typedef to the + * child struct, int etc it is defined as. In such cases, the type_id + * still represents the starting type while the struct btf_type * + * in our d->data.state points at the resolved type of the typedef. + */ + t = btf__type_by_id(d->btf, id); + if (!t) + return ""; + + /* + * The goal here is to build up the right number of pointer and + * array suffixes while ensuring the type name for a typedef + * is represented. Along the way we accumulate a list of + * BTF kinds we have encountered, since these will inform later + * display; for example, pointer types will not require an + * opening "{" for struct, we will just display the pointer value. + * + * We also want to accumulate the right number of pointer or array + * indices in the format string while iterating until we get to + * the typedef/pointee/array member target type. + * + * We start by pointing at the end of pointer and array suffix + * strings; as we accumulate pointers and arrays we move the pointer + * or array string backwards so it will show the expected number of + * '*' or '[]' for the type. BTF_SHOW_MAX_ITER of nesting of pointers + * and/or arrays and typedefs are supported as a precaution. + * + * We also want to get typedef name while proceeding to resolve + * type it points to so that we can add parentheses if it is a + * "typedef struct" etc. + * + * Qualifiers ("const", "volatile", "restrict") are simply skipped + * as they complicate simple type name display without adding much + * in the case of displaying a cast in front of the data to be + * displayed. + */ + for (i = 0; i < BTF_MAX_ITER; i++) { + + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_TYPEDEF: + if (!name) + name = btf_name_of(d, t->name_off); + kinds |= BTF_KIND_BIT(BTF_KIND_TYPEDEF); + id = t->type; + break; + case BTF_KIND_ARRAY: + kinds |= BTF_KIND_BIT(BTF_KIND_ARRAY); + parens = "["; + if (!t) + return ""; + array = btf_array(t); + if (array_suffix > array_suffixes) + array_suffix -= 2; + id = array->type; + break; + case BTF_KIND_PTR: + kinds |= BTF_KIND_BIT(BTF_KIND_PTR); + if (ptr_suffix > ptr_suffixes) + ptr_suffix -= 1; + id = t->type; + break; + default: + id = 0; + break; + } + if (!id) + break; + t = skip_mods(d->btf, id, NULL); + } + /* We may not be able to represent this type; bail to be safe */ + if (i == BTF_MAX_ITER) { + pr_warn("iters %d exceeded %d when displaying type name:[%u]\n", + i, BTF_MAX_ITER, id); + return ""; + } + + if (!name) + name = btf_name_of(d, t->name_off); + + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + prefix = BTF_INFO_KIND(t->info) == BTF_KIND_STRUCT ? + "struct" : "union"; + /* if it's an array of struct/union, parens is already set */ + if (!(kinds & (BTF_KIND_BIT(BTF_KIND_ARRAY)))) + parens = "{"; + break; + case BTF_KIND_ENUM: + prefix = "enum"; + break; + default: + break; + } + + /* pointer does not require parens */ + if (kinds & BTF_KIND_BIT(BTF_KIND_PTR)) + parens = ""; + /* typedef does not require struct/union/enum prefix */ + if (kinds & BTF_KIND_BIT(BTF_KIND_TYPEDEF)) + prefix = ""; + + if (!name) + name = ""; + + /* Even if we don't want type name info, we want parentheses etc */ + if (d->data.noname) + snprintf(d->data.state.name, sizeof(d->data.state.name), "%s", + parens); + else + snprintf(d->data.state.name, sizeof(d->data.state.name), + "%s%s%s(%s%s%s%s%s%s)%s", + /* first 3 strings comprise ".member = " */ + show_member ? "." : "", + show_member ? member : "", + show_member ? " = " : "", + /* ...next is our prefix (struct, enum, etc) */ + prefix, + strlen(prefix) > 0 && strlen(name) > 0 ? " " : "", + /* ...this is the type name itself */ + name, + /* ...suffixed by the appropriate '*', '[]' suffixes */ + strlen(name) > 0 && strlen(ptr_suffix) > 0 ? " " : "", + ptr_suffix, + array_suffix, parens); + + return d->data.state.name; +} + +static const char *btf_dump_data_indent(struct btf_dump *d) +{ + if (d->data.compact) + return ""; + return spfx(d->data.indent_lvl + d->data.state.depth); +} + +static const char *btf_dump_data_newline(struct btf_dump *d) +{ + return d->data.compact ? "" : "\n"; +} + +static const char *btf_dump_data_delim(struct btf_dump *d) +{ + if (d->data.state.depth == 0) + return ""; + + if (d->data.compact && + d->data.state.type && + BTF_INFO_KIND(d->data.state.type->info) == BTF_KIND_UNION) + return "|"; + + return ","; +} + +static void btf_dump_data_printf(struct btf_dump *d, + const char *fmt, ...) +{ + va_list args; + + /* + * Just checking if there is non-zero data to display at this depth, + * so nothing is displayed. + */ + if (d->data.state.depth_check) + return; + va_start(args, fmt); + d->printf_fn(d->opts.ctx, fmt, args); + va_end(args); +} + +/* Macros are used here as btf_type_value[s]() prepends and appends + * format specifiers to the format specifier passed in; these do the work of + * adding indentation, delimiters etc while the caller simply has to specify + * the type value(s) in the format specifier + value(s). + */ +#define btf_dump_emit_type_value(d, fmt, value) \ + do { \ + if ((value) != 0 || d->data.zero || \ + d->data.state.depth == 0) { \ + btf_dump_data_printf(d, "%s%s" fmt "%s%s", \ + btf_dump_data_indent(d), \ + btf_dump_data_name(d), \ + value, \ + btf_dump_data_delim(d), \ + btf_dump_data_newline(d)); \ + if (d->data.state.depth > \ + d->data.state.depth_to_show) \ + d->data.state.depth_to_show = \ + d->data.state.depth; \ + } \ + } while (0) + +#define btf_dump_emit_type_values(d, fmt, ...) \ + do { \ + btf_dump_data_printf(d, "%s%s" fmt "%s%s", \ + btf_dump_data_indent(d), \ + btf_dump_data_name(d), \ + __VA_ARGS__, \ + btf_dump_data_delim(d), \ + btf_dump_data_newline(d)); \ + if (d->data.state.depth > \ + d->data.state.depth_to_show) \ + d->data.state.depth_to_show = \ + d->data.state.depth; \ + } while (0) + +/* Set the type we are starting to show. */ +static void btf_dump_start_type(struct btf_dump *d, + const struct btf_type *t, + __u32 type_id) +{ + d->data.state.type = t; + d->data.state.type_id = type_id; + d->data.state.name[0] = '\0'; +} + +static void btf_dump_end_type(struct btf_dump *d) +{ + d->data.state.type = NULL; + d->data.state.type_id = 0; + d->data.state.name[0] = '\0'; +} + +static void btf_dump_start_aggr_type(struct btf_dump *d, + const struct btf_type *t, + __u32 type_id) +{ + btf_dump_start_type(d, t, type_id); + + btf_dump_data_printf(d, "%s%s%s", + btf_dump_data_indent(d), + btf_dump_data_name(d), + btf_dump_data_newline(d)); + d->data.state.depth++; +} + +static void btf_dump_end_aggr_type(struct btf_dump *d, + const char *suffix) +{ + d->data.state.depth--; + btf_dump_data_printf(d, "%s%s%s%s", + btf_dump_data_indent(d), + suffix, + btf_dump_data_delim(d), + btf_dump_data_newline(d)); + btf_dump_end_type(d); +} + +static void btf_dump_start_member(struct btf_dump *d, + const struct btf_member *m) +{ + d->data.state.member = m; +} + +static void btf_dump_start_array_member(struct btf_dump *d) +{ + d->data.state.array_member = 1; + btf_dump_start_member(d, NULL); +} + +static void btf_dump_end_member(struct btf_dump *d) +{ + d->data.state.member = NULL; +} + +static void btf_dump_end_array_member(struct btf_dump *d) +{ + d->data.state.array_member = 0; + btf_dump_end_member(d); +} + +static void btf_dump_start_array_type(struct btf_dump *d, + const struct btf_type *t, + __u32 type_id, + __u16 array_encoding) +{ + d->data.state.array_encoding = array_encoding; + d->data.state.array_terminated = 0; + btf_dump_start_aggr_type(d, t, type_id); +} + +static void btf_dump_end_array_type(struct btf_dump *d) +{ + d->data.state.array_encoding = 0; + d->data.state.array_terminated = 0; + btf_dump_end_aggr_type(d, "]"); +} + +static void btf_dump_start_struct_type(struct btf_dump *d, + const struct btf_type *t, + __u32 type_id) +{ + btf_dump_start_aggr_type(d, t, type_id); +} + +static void btf_dump_end_struct_type(struct btf_dump *d) +{ + btf_dump_end_aggr_type(d, "}"); +} + +static void btf_dump_emit_df_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + btf_dump_data_printf(d, "", + BTF_INFO_KIND(t->info)); +} + +static void btf_dump_emit_int128(struct btf_dump *d, void *data) +{ + /* data points to a __int128 number. + * Suppose + * int128_num = *(__int128 *)data; + * The below formulas shows what upper_num and lower_num represents: + * upper_num = int128_num >> 64; + * lower_num = int128_num & 0xffffffffFFFFFFFFULL; + */ + __u64 upper_num, lower_num; + +#ifdef __BIG_ENDIAN_BITFIELD + upper_num = *(__u64 *)data; + lower_num = *(__u64 *)(data + 8); +#else + upper_num = *(__u64 *)(data + 8); + lower_num = *(__u64 *)data; +#endif + if (upper_num == 0) + btf_dump_emit_type_value(d, "0x%llx", lower_num); + else + btf_dump_emit_type_values(d, "0x%llx%016llx", upper_num, + lower_num); +} + +static void btf_int128_shift(__u64 *print_num, __u16 left_shift_bits, + __u16 right_shift_bits) +{ + __u64 upper_num, lower_num; + +#ifdef __BIG_ENDIAN_BITFIELD + upper_num = print_num[0]; + lower_num = print_num[1]; +#else + upper_num = print_num[1]; + lower_num = print_num[0]; +#endif + + /* shake out un-needed bits by shift/or operations */ + if (left_shift_bits >= 64) { + upper_num = lower_num << (left_shift_bits - 64); + lower_num = 0; + } else { + upper_num = (upper_num << left_shift_bits) | + (lower_num >> (64 - left_shift_bits)); + lower_num = lower_num << left_shift_bits; + } + + if (right_shift_bits >= 64) { + lower_num = upper_num >> (right_shift_bits - 64); + upper_num = 0; + } else { + lower_num = (lower_num >> right_shift_bits) | + (upper_num << (64 - right_shift_bits)); + upper_num = upper_num >> right_shift_bits; + } + +#ifdef __BIG_ENDIAN_BITFIELD + print_num[0] = upper_num; + print_num[1] = lower_num; +#else + print_num[0] = lower_num; + print_num[1] = upper_num; +#endif +} + +static void btf_dump_emit_bitfield_data(struct btf_dump *d, + void *data, + __u8 bits_offset, + __u8 nr_bits) +{ + __u16 left_shift_bits, right_shift_bits; + __u8 nr_copy_bytes; + __u8 nr_copy_bits; + __u64 print_num[2] = {}; + + nr_copy_bits = nr_bits + bits_offset; + nr_copy_bytes = BITS_ROUNDUP_BYTES(nr_copy_bits); + + memcpy(print_num, data, nr_copy_bytes); + +#ifdef __BIG_ENDIAN_BITFIELD + left_shift_bits = bits_offset; +#else + left_shift_bits = BITS_PER_U128 - nr_copy_bits; +#endif + right_shift_bits = BITS_PER_U128 - nr_bits; + + btf_int128_shift(print_num, left_shift_bits, right_shift_bits); + btf_dump_emit_int128(d, print_num); +} + +static void btf_dump_emit_int_bits(struct btf_dump *d, + const struct btf_type *t, + void *data, + __u8 bits_offset) +{ + __u32 int_data = btf_int(t); + __u8 nr_bits = BTF_INT_BITS(int_data); + __u8 total_bits_offset; + + /* + * bits_offset is at most 7. + * BTF_INT_OFFSET() cannot exceed 128 bits. + */ + total_bits_offset = bits_offset + BTF_INT_OFFSET(int_data); + data += BITS_ROUNDDOWN_BYTES(total_bits_offset); + bits_offset = BITS_PER_BYTE_MASKED(total_bits_offset); + btf_dump_emit_bitfield_data(d, data, bits_offset, nr_bits); +} + +static void btf_dump_emit_int_data(struct btf_dump *d, + const struct btf_type *t, + __u32 type_id, + void *data, + __u8 bits_offset) +{ + __u32 int_data = btf_int(t); + __u8 encoding = BTF_INT_ENCODING(int_data); + bool sign = encoding & BTF_INT_SIGNED; + __u8 nr_bits = BTF_INT_BITS(int_data); + + btf_dump_start_type(d, t, type_id); + + if (bits_offset || BTF_INT_OFFSET(int_data) || + BITS_PER_BYTE_MASKED(nr_bits)) { + btf_dump_emit_int_bits(d, t, data, bits_offset); + goto out; + } + + switch (nr_bits) { + case 128: + btf_dump_emit_int128(d, data); + break; + case 64: + if (sign) + btf_dump_emit_type_value(d, "%lld", *(__s64 *)data); + else + btf_dump_emit_type_value(d, "%llu", *(__u64 *)data); + break; + case 32: + if (sign) + btf_dump_emit_type_value(d, "%d", *(__s32 *)data); + else + btf_dump_emit_type_value(d, "%u", *(__u32 *)data); + break; + case 16: + if (sign) + btf_dump_emit_type_value(d, "%d", *(__s16 *)data); + else + btf_dump_emit_type_value(d, "%u", *(__u16 *)data); + break; + case 8: + if (d->data.state.array_encoding == BTF_INT_CHAR) { + /* check for null terminator */ + if (d->data.state.array_terminated) + break; + if (*(char *)data == '\0') { + d->data.state.array_terminated = 1; + break; + } + if (isprint(*(char *)data)) { + btf_dump_emit_type_value(d, "'%c'", + *(char *)data); + break; + } + } + if (sign) + btf_dump_emit_type_value(d, "%d", *(__s8 *)data); + else + btf_dump_emit_type_value(d, "%u", *(__u8 *)data); + break; + default: + btf_dump_emit_int_bits(d, t, data, bits_offset); + break; + } +out: + btf_dump_end_type(d); +} + +static void btf_dump_emit_modifier_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + t = skip_mods_and_typedefs(d->btf, id, NULL); + __btf_dump_emit_type_data(d, t, id, data, bits_offset); +} + +static void btf_dump_emit_var_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + __u32 linkage = btf_var(t)->linkage; + + btf_dump_data_printf(d, "%s%s =", + linkage ? "" : "static ", + btf_name_of(d, t->name_off)); + t = btf__type_by_id(d->btf, t->type); + __btf_dump_emit_type_data(d, t, t->type, data, bits_offset); +} + +static void __btf_dump_emit_array_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_array *array = btf_array(t); + const struct btf_type *elem_type; + __u32 i, elem_size = 0, elem_type_id; + __u16 encoding = 0; + + elem_type_id = array->type; + elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL); + if (elem_type && btf_has_size(elem_type)) + elem_size = elem_type->size; + + if (elem_type && btf_is_int(elem_type)) { + __u32 int_type = btf_int(elem_type); + + encoding = BTF_INT_ENCODING(int_type); + + /* + * BTF_INT_CHAR encoding never seems to be set for + * char arrays, so if size is 1 and element is + * printable as a char, we'll do that. + */ + if (elem_size == 1) + encoding = BTF_INT_CHAR; + } + + btf_dump_start_array_type(d, t, id, encoding); + + if (!elem_type) + goto out; + + for (i = 0; i < array->nelems; i++) { + + btf_dump_start_array_member(d); + + __btf_dump_emit_type_data(d, elem_type, elem_type_id, + data, bits_offset); + data += elem_size; + + btf_dump_end_array_member(d); + + if (d->data.state.array_terminated) + break; + } +out: + btf_dump_end_array_type(d); +} + +static void btf_dump_emit_array_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_member *m = d->data.state.member; + + /* + * First check if any members would be shown (are non-zero). + * See comments above "struct btf_dump_data" definition for more + * details on how this works at a high-level. + */ + if (d->data.state.depth > 0 && !d->data.zero) { + if (!d->data.state.depth_check) { + d->data.state.depth_check = d->data.state.depth + 1; + d->data.state.depth_to_show = 0; + } + __btf_dump_emit_array_data(d, t, id, data, bits_offset); + d->data.state.member = m; + + if (d->data.state.depth_check != d->data.state.depth + 1) + return; + d->data.state.depth_check = 0; + + if (d->data.state.depth_to_show <= d->data.state.depth) + return; + /* + * Reaching here indicates we have recursed and found + * non-zero array member(s). + */ + } + __btf_dump_emit_array_data(d, t, id, data, bits_offset); +} + +#define for_each_member(i, struct_type, member) \ + for (i = 0, member = btf_members(struct_type); \ + i < btf_vlen(struct_type); \ + i++, member++) + +static void __btf_dump_emit_struct_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_member *member; + __u32 i; + + btf_dump_start_struct_type(d, t, id); + + for_each_member(i, t, member) { + const struct btf_type *member_type; + __u32 member_offset, bitfield_size; + __u32 bytes_offset; + __u8 bits8_offset; + + member_type = btf__type_by_id(d->btf, member->type); + btf_dump_start_member(d, member); + + member_offset = btf_member_bit_offset(t, i); + bitfield_size = btf_member_bitfield_size(t, i); + bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset); + bits8_offset = BITS_PER_BYTE_MASKED(member_offset); + if (bitfield_size) { + btf_dump_start_type(d, member_type, member->type); + btf_dump_emit_bitfield_data(d, + data + bytes_offset, + bits8_offset, + bitfield_size); + btf_dump_end_type(d); + } else { + __btf_dump_emit_type_data(d, member_type, member->type, + data + bytes_offset, bits8_offset); + } + btf_dump_end_member(d); + } + btf_dump_end_struct_type(d); +} + +static void btf_dump_emit_struct_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_member *m = d->data.state.member; + + /* + * First check if any members would be shown (are non-zero). + * See comments above "struct btf_dump_data" definition for more + * details on how this works at a high-level. + */ + if (d->data.state.depth > 0 && !d->data.zero) { + if (!d->data.state.depth_check) { + d->data.state.depth_check = d->data.state.depth + 1; + d->data.state.depth_to_show = 0; + } + __btf_dump_emit_struct_data(d, t, id, data, bits_offset); + /* Restore saved member data here */ + d->data.state.member = m; + if (d->data.state.depth_check != d->data.state.depth + 1) + return; + d->data.state.depth_check = 0; + + if (d->data.state.depth_to_show <= d->data.state.depth) + return; + /* + * Reaching here indicates we have recursed and found + * non-zero child values. + */ + } + + __btf_dump_emit_struct_data(d, t, id, data, bits_offset); +} + +static void btf_dump_emit_ptr_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + btf_dump_start_type(d, t, id); + + btf_dump_emit_type_value(d, "%p", *(void **)data); + btf_dump_end_type(d); +} + +static void btf_dump_emit_enum_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_enum *enums = btf_enum(t); + __s64 value; + __u16 i; + + btf_dump_start_type(d, t, id); + + switch (t->size) { + case 8: + value = *(__s64 *)data; + break; + case 4: + value = *(__s32 *)data; + break; + case 2: + value = *(__s16 *)data; + break; + case 1: + value = *(__s8 *)data; + break; + default: + pr_warn("unexpected size %d for enum, id:[%u]\n", t->size, + id); + d->data.state.err = -EINVAL; + return; + } + + for (i = 0; i < btf_vlen(t); i++) { + if (value == enums[i].val) { + btf_dump_emit_type_value(d, "%s", + btf_name_of(d, + enums[i].name_off)); + btf_dump_end_type(d); + return; + } + } + + btf_dump_emit_type_value(d, "%d", value); + btf_dump_end_type(d); +} + +#define for_each_vsi(i, struct_type, member) \ + for (i = 0, member = btf_var_secinfos(struct_type); \ + i < btf_vlen(struct_type); \ + i++, member++) + +static void btf_dump_emit_datasec_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + const struct btf_var_secinfo *vsi; + const struct btf_type *var; + __u32 i; + + btf_dump_start_type(d, t, id); + + btf_dump_emit_type_value(d, "section (\"%s\") = {", + btf_name_of(d, t->name_off)); + for_each_vsi(i, t, vsi) { + var = btf__type_by_id(d->btf, vsi->type); + if (i) + btf_dump_data_printf(d, ","); + __btf_dump_emit_type_data(d, var, vsi->type, + data + vsi->offset, + bits_offset); + } + btf_dump_end_type(d); +} + +typedef void (*btf_dump_emit_kind_data)(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset); + +static btf_dump_emit_kind_data dump_emit_kind_data[NR_BTF_KINDS] = { + &btf_dump_emit_df_data, + &btf_dump_emit_int_data, + &btf_dump_emit_ptr_data, + &btf_dump_emit_array_data, + &btf_dump_emit_struct_data, + &btf_dump_emit_struct_data, + &btf_dump_emit_enum_data, + &btf_dump_emit_df_data, + &btf_dump_emit_modifier_data, + &btf_dump_emit_modifier_data, + &btf_dump_emit_modifier_data, + &btf_dump_emit_modifier_data, + &btf_dump_emit_df_data, + &btf_dump_emit_df_data, + &btf_dump_emit_var_data, + &btf_dump_emit_datasec_data, +}; + +static void __btf_dump_emit_type_data(struct btf_dump *d, + const struct btf_type *t, + __u32 id, + void *data, + __u8 bits_offset) +{ + dump_emit_kind_data[BTF_INFO_KIND(t->info)](d, t, id, data, + bits_offset); +} + +static void btf_dump_emit_type_data(struct btf_dump *d, __u32 id, void *data) +{ + const struct btf_type *t = btf__type_by_id(d->btf, id); + + memset(&d->data.state, 0, sizeof(d->data.state)); + + if (!t) { + pr_warn("no type info, id [%u]\n", id); + d->data.state.err = -EINVAL; + return; + } + + __btf_dump_emit_type_data(d, t, id, data, 0); +} + +int btf_dump__emit_type_data(struct btf_dump *d, __u32 id, + const struct btf_dump_emit_type_data_opts *opts, + void *data) +{ + int err; + + if (!OPTS_VALID(opts, btf_dump_emit_type_data_opts)) + return -EINVAL; + + d->data.indent_lvl = OPTS_GET(opts, indent_level, 0); + d->data.compact = OPTS_GET(opts, compact, false); + d->data.noname = OPTS_GET(opts, noname, false); + d->data.zero = OPTS_GET(opts, zero, false); + btf_dump_emit_type_data(d, id, data); + err = d->data.state.err; + memset(&d->data, 0, sizeof(d->data)); + return err; +} diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 1c0fd2d..b81c069 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -350,3 +350,8 @@ LIBBPF_0.3.0 { xsk_setup_xdp_prog; xsk_socket__update_xskmap; } LIBBPF_0.2.0; + +LIBBPF_0.4.0 { + global: + btf_dump__emit_type_data; +} LIBBPF_0.3.0; From patchwork Sun Jan 17 22:16:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 12025857 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 X-Spam-Level: X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DF9E0C433E0 for ; Sun, 17 Jan 2021 22:22:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AC03D222B3 for ; Sun, 17 Jan 2021 22:22:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730404AbhAQWWC (ORCPT ); Sun, 17 Jan 2021 17:22:02 -0500 Received: from aserp2120.oracle.com ([141.146.126.78]:55106 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730366AbhAQWVs (ORCPT ); Sun, 17 Jan 2021 17:21:48 -0500 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMKo8H075267; Sun, 17 Jan 2021 22:20:50 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=aVLOHaoBygqp0kGdrn1OdqwsPVrpETXymT4FNLNKUF0=; b=pdXcSMkSNGmow881DUP6CJ7LIbt+03zQBA3dkJ5BbS7ML8spgf9+AF5q+imRg0ZH6CAD f/yQb9FGbCM44I3dOcSjLr53QIQ+XdaKekr++ryI839CdY7yXdS5wFtvhr01qykk3mpE kP43SM8hxd2JzpEKbOUCvJifR90vt7/nfDTjKTDChQY2hlTOHKUu0NbButQDHnYDx0Ov TSlrZKojtkjboaoZ/ghzHy8cM/H+eWb8+qM3ONXWKYqPt4uCDTDQFQoSePBMIUgBWRHd eqQ+fsd5ukB6IYgy6xBkB5+plqqlAfRqQGOWMcXI3fsyQECs0FlIt9JxjckPImcc8VXr kg== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by aserp2120.oracle.com with ESMTP id 363r3kjvrh-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:50 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10HMKnIc075485; Sun, 17 Jan 2021 22:20:49 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userp3020.oracle.com with ESMTP id 3649wnyxuj-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 17 Jan 2021 22:20:49 +0000 Received: from abhmp0003.oracle.com (abhmp0003.oracle.com [141.146.116.9]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 10HMKgvM028303; Sun, 17 Jan 2021 22:20:42 GMT Received: from localhost.localdomain (/95.45.14.174) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 17 Jan 2021 14:20:42 -0800 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org Cc: kafai@fb.com, songliubraving@fb.com, yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org, morbo@google.com, shuah@kernel.org, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Alan Maguire Subject: [PATCH v2 bpf-next 4/4] selftests/bpf: add dump type data tests to btf dump tests Date: Sun, 17 Jan 2021 22:16:04 +0000 Message-Id: <1610921764-7526-5-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> References: <1610921764-7526-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxscore=0 mlxlogscore=999 bulkscore=0 spamscore=0 phishscore=0 malwarescore=0 adultscore=0 suspectscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170140 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9867 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxlogscore=999 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 priorityscore=1501 mlxscore=0 malwarescore=0 phishscore=0 suspectscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101170140 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Test various type data dumping operations by comparing expected format with the dumped string; an snprintf-style printf function is used to record the string dumped. Signed-off-by: Alan Maguire --- tools/testing/selftests/bpf/prog_tests/btf_dump.c | 233 ++++++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index c60091e..262561f4 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -232,6 +232,237 @@ void test_btf_dump_incremental(void) btf__free(btf); } +#define STRSIZE 2048 +#define EXPECTED_STRSIZE 256 + +void btf_dump_snprintf(void *ctx, const char *fmt, va_list args) +{ + char *s = ctx, new[STRSIZE]; + + vsnprintf(new, STRSIZE, fmt, args); + strncat(s, new, STRSIZE); + vfprintf(ctx, fmt, args); +} + +/* skip "enum "/"struct " prefixes */ +#define SKIP_PREFIX(_typestr, _prefix) \ + do { \ + if (strstr(_typestr, _prefix) == _typestr) \ + _typestr += strlen(_prefix) + 1; \ + } while (0) + +int btf_dump_data(struct btf *btf, struct btf_dump *d, + char *ptrtype, __u64 flags, void *ptr, + char *str, char *expectedval) +{ + struct btf_dump_emit_type_data_opts opts = { 0 }; + int ret = 0, cmp; + __s32 type_id; + + opts.sz = sizeof(opts); + opts.compact = true; + if (flags & BTF_F_NONAME) + opts.noname = true; + if (flags & BTF_F_ZERO) + opts.zero = true; + SKIP_PREFIX(ptrtype, "enum"); + SKIP_PREFIX(ptrtype, "struct"); + SKIP_PREFIX(ptrtype, "union"); + type_id = btf__find_by_name(btf, ptrtype); + if (CHECK(type_id <= 0, "find type id", + "no '%s' in BTF: %d\n", ptrtype, type_id)) { + ret = -ENOENT; + goto err; + } + str[0] = '\0'; + ret = btf_dump__emit_type_data(d, type_id, &opts, ptr); + if (CHECK(ret < 0, "btf_dump__emit_type_data", + "failed: %d\n", ret)) + goto err; + + cmp = strncmp(str, expectedval, EXPECTED_STRSIZE); + if (CHECK(cmp, "ensure expected/actual match", + "'%s' does not match expected '%s': %d\n", + str, expectedval, cmp)) + ret = -EFAULT; + +err: + if (ret) + btf_dump__free(d); + return ret; +} + +#define TEST_BTF_DUMP_DATA(_b, _d, _str, _type, _flags, _expected, ...) \ + do { \ + char _expectedval[EXPECTED_STRSIZE] = _expected; \ + char __ptrtype[64] = #_type; \ + char *_ptrtype = (char *)__ptrtype; \ + static _type _ptrdata = __VA_ARGS__; \ + void *_ptr = &_ptrdata; \ + \ + if (btf_dump_data(_b, _d, _ptrtype, _flags, _ptr, \ + _str, _expectedval)) \ + return; \ + } while (0) + +/* Use where expected data string matches its stringified declaration */ +#define TEST_BTF_DUMP_DATA_C(_b, _d, _str, _type, _opts, ...) \ + TEST_BTF_DUMP_DATA(_b, _d, _str, _type, _opts, \ + "(" #_type ")" #__VA_ARGS__, __VA_ARGS__) + +void test_btf_dump_data(void) +{ + struct btf *btf = libbpf_find_kernel_btf(); + char str[STRSIZE]; + struct btf_dump_opts opts = { .ctx = str }; + struct btf_dump *d; + + if (CHECK(!btf, "get kernel BTF", "no kernel BTF found")) + return; + + d = btf_dump__new(btf, NULL, &opts, btf_dump_snprintf); + + if (CHECK(!d, "new dump", "could not create BTF dump")) + return; + + /* Verify type display for various types. */ + + /* simple int */ + TEST_BTF_DUMP_DATA_C(btf, d, str, int, 0, 1234); + TEST_BTF_DUMP_DATA(btf, d, str, int, BTF_F_NONAME, "1234", 1234); + + /* zero value should be printed at toplevel */ + TEST_BTF_DUMP_DATA(btf, d, str, int, 0, "(int)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, int, BTF_F_NONAME, "0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, int, BTF_F_ZERO, "(int)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, int, BTF_F_NONAME | BTF_F_ZERO, + "0", 0); + TEST_BTF_DUMP_DATA_C(btf, d, str, int, 0, -4567); + TEST_BTF_DUMP_DATA(btf, d, str, int, BTF_F_NONAME, "-4567", -4567); + + /* simple char */ + TEST_BTF_DUMP_DATA_C(btf, d, str, char, 0, 100); + TEST_BTF_DUMP_DATA(btf, d, str, char, BTF_F_NONAME, "100", 100); + /* zero value should be printed at toplevel */ + TEST_BTF_DUMP_DATA(btf, d, str, char, 0, "(char)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, char, BTF_F_NONAME, "0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, char, BTF_F_ZERO, "(char)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, char, BTF_F_NONAME | BTF_F_ZERO, + "0", 0); + + /* simple typedef */ + TEST_BTF_DUMP_DATA_C(btf, d, str, uint64_t, 0, 100); + TEST_BTF_DUMP_DATA(btf, d, str, u64, BTF_F_NONAME, "1", 1); + /* zero value should be printed at toplevel */ + TEST_BTF_DUMP_DATA(btf, d, str, u64, 0, "(u64)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, u64, BTF_F_NONAME, "0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, u64, BTF_F_ZERO, "(u64)0", 0); + TEST_BTF_DUMP_DATA(btf, d, str, u64, BTF_F_NONAME | BTF_F_ZERO, + "0", 0); + + /* typedef struct */ + TEST_BTF_DUMP_DATA_C(btf, d, str, atomic_t, 0, {.counter = (int)1,}); + TEST_BTF_DUMP_DATA(btf, d, str, atomic_t, BTF_F_NONAME, "{1,}", + {.counter = 1,}); + /* typedef with 0 value should be printed at toplevel */ + TEST_BTF_DUMP_DATA(btf, d, str, atomic_t, 0, "(atomic_t){}", + {.counter = 0,}); + TEST_BTF_DUMP_DATA(btf, d, str, atomic_t, BTF_F_NONAME, "{}", + {.counter = 0,}); + TEST_BTF_DUMP_DATA(btf, d, str, atomic_t, BTF_F_ZERO, + "(atomic_t){.counter = (int)0,}", + {.counter = 0,}); + TEST_BTF_DUMP_DATA(btf, d, str, atomic_t, BTF_F_NONAME | BTF_F_ZERO, + "{0,}", {.counter = 0,}); + /* enum where enum value does (and does not) exist */ + TEST_BTF_DUMP_DATA_C(btf, d, str, enum bpf_cmd, 0, BPF_MAP_CREATE); + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, 0, + "(enum bpf_cmd)BPF_MAP_CREATE", 0); + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, BTF_F_NONAME, + "BPF_MAP_CREATE", + BPF_MAP_CREATE); + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, + BTF_F_NONAME | BTF_F_ZERO, + "BPF_MAP_CREATE", 0); + + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, BTF_F_ZERO, + "(enum bpf_cmd)BPF_MAP_CREATE", + BPF_MAP_CREATE); + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, + BTF_F_NONAME | BTF_F_ZERO, + "BPF_MAP_CREATE", BPF_MAP_CREATE); + TEST_BTF_DUMP_DATA_C(btf, d, str, enum bpf_cmd, 0, 2000); + TEST_BTF_DUMP_DATA(btf, d, str, enum bpf_cmd, BTF_F_NONAME, + "2000", 2000); + + /* simple struct */ + TEST_BTF_DUMP_DATA_C(btf, d, str, struct btf_enum, 0, + {.name_off = (__u32)3,.val = (__s32)-1,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, BTF_F_NONAME, + "{3,-1,}", + { .name_off = 3, .val = -1,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, BTF_F_NONAME, "{-1,}", + { .name_off = 0, .val = -1,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, + BTF_F_NONAME | BTF_F_ZERO, + "{0,-1,}", + { .name_off = 0, .val = -1,}); + /* empty struct should be printed */ + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, 0, + "(struct btf_enum){}", + { .name_off = 0, .val = 0,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, BTF_F_NONAME, "{}", + { .name_off = 0, .val = 0,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct btf_enum, BTF_F_ZERO, + "(struct btf_enum){.name_off = (__u32)0,.val = (__s32)0,}", + { .name_off = 0, .val = 0,}); + + /* struct with pointers */ + TEST_BTF_DUMP_DATA(btf, d, str, struct list_head, 0, + "(struct list_head){.next = (struct list_head *)0x1,}", + { .next = (struct list_head *)1 }); + /* NULL pointer should not be displayed */ + TEST_BTF_DUMP_DATA(btf, d, str, struct list_head, 0, + "(struct list_head){}", + { .next = (struct list_head *)0 }); + /* struct with char array */ + TEST_BTF_DUMP_DATA(btf, d, str, struct bpf_prog_info, 0, + "(struct bpf_prog_info){.name = (char[])['f','o','o',],}", + { .name = "foo",}); + TEST_BTF_DUMP_DATA(btf, d, str, struct bpf_prog_info, BTF_F_NONAME, + "{['f','o','o',],}", + {.name = "foo",}); + /* leading null char means do not display string */ + TEST_BTF_DUMP_DATA(btf, d, str, struct bpf_prog_info, 0, + "(struct bpf_prog_info){}", + {.name = {'\0', 'f', 'o', 'o'}}); + /* handle non-printable characters */ + TEST_BTF_DUMP_DATA(btf, d, str, struct bpf_prog_info, 0, + "(struct bpf_prog_info){.name = (char[])[1,2,3,],}", + { .name = {1, 2, 3, 0}}); + + /* struct with non-char array */ + TEST_BTF_DUMP_DATA(btf, d, str, struct __sk_buff, 0, + "(struct __sk_buff){.cb = (__u32[])[1,2,3,4,5,],}", + { .cb = {1, 2, 3, 4, 5,},}); + TEST_BTF_DUMP_DATA(btf, d, str, struct __sk_buff, BTF_F_NONAME, + "{[1,2,3,4,5,],}", + { .cb = { 1, 2, 3, 4, 5},}); + /* For non-char, arrays, show non-zero values only */ + TEST_BTF_DUMP_DATA(btf, d, str, struct __sk_buff, 0, + "(struct __sk_buff){.cb = (__u32[])[1,],}", + { .cb = { 0, 0, 1, 0, 0},}); + + /* struct with bitfields */ + TEST_BTF_DUMP_DATA_C(btf, d, str, struct bpf_insn, 0, + {.code = (__u8)1,.dst_reg = (__u8)0x2,.src_reg = (__u8)0x3,.off = (__s16)4,.imm = (__s32)5,}); + TEST_BTF_DUMP_DATA(btf, d, str, struct bpf_insn, BTF_F_NONAME, + "{1,0x2,0x3,4,5,}", + { .code = 1, .dst_reg = 0x2, .src_reg = 0x3, .off = 4, + .imm = 5,}); +} + + void test_btf_dump() { int i; @@ -245,4 +476,6 @@ void test_btf_dump() { } if (test__start_subtest("btf_dump: incremental")) test_btf_dump_incremental(); + if (test__start_subtest("btf_dump: data")) + test_btf_dump_data(); }