diff mbox series

[v2,bpf-next] selftests/bpf: merge most of test_btf into test_progs

Message ID 20200915014341.2949692-1-andriin@fb.com
State Changes Requested
Delegated to: BPF Maintainers
Headers show
Series [v2,bpf-next] selftests/bpf: merge most of test_btf into test_progs | expand

Commit Message

Andrii Nakryiko Sept. 15, 2020, 1:43 a.m. UTC
Move almost 200 tests from test_btf into test_progs framework to be exercised
regularly. Pretty-printing tests were left alone and renamed into
test_btf_pprint because they are very slow and were not even executed by
default with test_btf.

All the test_btf tests that were moved are modeled as proper sub-tests in
test_progs framework for ease of debugging and reporting.

No functional or behavioral changes were intended, I tried to preserve
original behavior as close to the original as possible. `test_progs -v` will
activate "always_log" flag to emit BTF validation log.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---

v1->v2:
 - pretty-print BTF tests were renamed test_btf -> test_btf_pprint, which
   allowed GIT to detect that majority of  test_btf code was moved into
   prog_tests/btf.c; so diff is much-much smaller;

 tools/testing/selftests/bpf/.gitignore        |    2 +-
 .../bpf/{test_btf.c => prog_tests/btf.c}      | 1069 +----------------
 tools/testing/selftests/bpf/test_btf_pprint.c |  969 +++++++++++++++
 3 files changed, 1033 insertions(+), 1007 deletions(-)
 rename tools/testing/selftests/bpf/{test_btf.c => prog_tests/btf.c} (85%)
 create mode 100644 tools/testing/selftests/bpf/test_btf_pprint.c

Comments

Andrii Nakryiko Sept. 15, 2020, 5:26 a.m. UTC | #1
On Mon, Sep 14, 2020 at 6:43 PM Andrii Nakryiko <andriin@fb.com> wrote:
>
> Move almost 200 tests from test_btf into test_progs framework to be exercised
> regularly. Pretty-printing tests were left alone and renamed into
> test_btf_pprint because they are very slow and were not even executed by
> default with test_btf.
>
> All the test_btf tests that were moved are modeled as proper sub-tests in
> test_progs framework for ease of debugging and reporting.
>
> No functional or behavioral changes were intended, I tried to preserve
> original behavior as close to the original as possible. `test_progs -v` will
> activate "always_log" flag to emit BTF validation log.
>
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ---
>
> v1->v2:
>  - pretty-print BTF tests were renamed test_btf -> test_btf_pprint, which
>    allowed GIT to detect that majority of  test_btf code was moved into
>    prog_tests/btf.c; so diff is much-much smaller;
>
>  tools/testing/selftests/bpf/.gitignore        |    2 +-
>  .../bpf/{test_btf.c => prog_tests/btf.c}      | 1069 +----------------
>  tools/testing/selftests/bpf/test_btf_pprint.c |  969 +++++++++++++++
>  3 files changed, 1033 insertions(+), 1007 deletions(-)
>  rename tools/testing/selftests/bpf/{test_btf.c => prog_tests/btf.c} (85%)
>  create mode 100644 tools/testing/selftests/bpf/test_btf_pprint.c

Looks like I forgot to check in a trivial Makefile change (test_btf ->
test_btf_pprint), but I'll hold off until we decided where
pretty-print BTF tests should live.

[...]
Alexei Starovoitov Sept. 15, 2020, 11:37 p.m. UTC | #2
On Mon, Sep 14, 2020 at 06:43:41PM -0700, Andrii Nakryiko wrote:
> Move almost 200 tests from test_btf into test_progs framework to be exercised
> regularly. Pretty-printing tests were left alone and renamed into
> test_btf_pprint because they are very slow and were not even executed by
> default with test_btf.

I think would be good to run them by default.
The following trivial tweak makes them fast:
diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c
index c75fc6447186..589afd4f0e47 100644
--- a/tools/testing/selftests/bpf/test_btf.c
+++ b/tools/testing/selftests/bpf/test_btf.c
@@ -4428,7 +4428,7 @@ static struct btf_raw_test pprint_test_template[] = {
        .value_size = sizeof(struct pprint_mapv),
        .key_type_id = 3,       /* unsigned int */
        .value_type_id = 16,    /* struct pprint_mapv */
-       .max_entries = 128 * 1024,
+       .max_entries = 128,
 },

 {
@@ -4493,7 +4493,7 @@ static struct btf_raw_test pprint_test_template[] = {
        .value_size = sizeof(struct pprint_mapv),
        .key_type_id = 3,       /* unsigned int */
        .value_type_id = 16,    /* struct pprint_mapv */
-       .max_entries = 128 * 1024,
+       .max_entries = 128,
 },

 {
@@ -4564,7 +4564,7 @@ static struct btf_raw_test pprint_test_template[] = {
        .value_size = sizeof(struct pprint_mapv),
        .key_type_id = 3,       /* unsigned int */
        .value_type_id = 16,    /* struct pprint_mapv */
-       .max_entries = 128 * 1024,
+       .max_entries = 128,
 },

Martin,
do you remember why you picked such large numbers of entries
for the test?
If I read the code correctly smaller number doesn't reduce the test coverage.

> All the test_btf tests that were moved are modeled as proper sub-tests in
> test_progs framework for ease of debugging and reporting.
> 
> No functional or behavioral changes were intended, I tried to preserve
> original behavior as close to the original as possible. `test_progs -v` will
> activate "always_log" flag to emit BTF validation log.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ---
> 
> v1->v2:
>  - pretty-print BTF tests were renamed test_btf -> test_btf_pprint, which
>    allowed GIT to detect that majority of  test_btf code was moved into
>    prog_tests/btf.c; so diff is much-much smaller;

Thanks. I hope with addition to pprint test the diff will be even smaller.
I think it's worth to investigate why they're failing if moved to test_progs.
I think they're the only tests that exercise seq_read logic.
Clearly the bug:
[   25.960993] WARNING: CPU: 2 PID: 1995 at kernel/bpf/hashtab.c:717 htab_map_get_next_key+0x7fc/0xab0
is still there.
If pprint tests were part of test_progs we would have caught that earlier.

Yonghong,
please take a look at that issue.
Andrii Nakryiko Sept. 16, 2020, 12:14 a.m. UTC | #3
On Tue, Sep 15, 2020 at 4:37 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Mon, Sep 14, 2020 at 06:43:41PM -0700, Andrii Nakryiko wrote:
> > Move almost 200 tests from test_btf into test_progs framework to be exercised
> > regularly. Pretty-printing tests were left alone and renamed into
> > test_btf_pprint because they are very slow and were not even executed by
> > default with test_btf.
>
> I think would be good to run them by default.
> The following trivial tweak makes them fast:

Sounds good, I'll debug why it was failing now that I can run it reasonably.

> diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c
> index c75fc6447186..589afd4f0e47 100644
> --- a/tools/testing/selftests/bpf/test_btf.c
> +++ b/tools/testing/selftests/bpf/test_btf.c
> @@ -4428,7 +4428,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
>
>  {
> @@ -4493,7 +4493,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
>
>  {
> @@ -4564,7 +4564,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
>
> Martin,
> do you remember why you picked such large numbers of entries
> for the test?
> If I read the code correctly smaller number doesn't reduce the test coverage.
>
> > All the test_btf tests that were moved are modeled as proper sub-tests in
> > test_progs framework for ease of debugging and reporting.
> >
> > No functional or behavioral changes were intended, I tried to preserve
> > original behavior as close to the original as possible. `test_progs -v` will
> > activate "always_log" flag to emit BTF validation log.
> >
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> >
> > v1->v2:
> >  - pretty-print BTF tests were renamed test_btf -> test_btf_pprint, which
> >    allowed GIT to detect that majority of  test_btf code was moved into
> >    prog_tests/btf.c; so diff is much-much smaller;
>
> Thanks. I hope with addition to pprint test the diff will be even smaller.
> I think it's worth to investigate why they're failing if moved to test_progs.
> I think they're the only tests that exercise seq_read logic.
> Clearly the bug:
> [   25.960993] WARNING: CPU: 2 PID: 1995 at kernel/bpf/hashtab.c:717 htab_map_get_next_key+0x7fc/0xab0
> is still there.
> If pprint tests were part of test_progs we would have caught that earlier.

yep, with pretty-printing tests in test_btf diff should be very small

>
> Yonghong,
> please take a look at that issue.
Martin KaFai Lau Sept. 16, 2020, 12:20 a.m. UTC | #4
On Tue, Sep 15, 2020 at 04:37:50PM -0700, Alexei Starovoitov wrote:
> On Mon, Sep 14, 2020 at 06:43:41PM -0700, Andrii Nakryiko wrote:
> > Move almost 200 tests from test_btf into test_progs framework to be exercised
> > regularly. Pretty-printing tests were left alone and renamed into
> > test_btf_pprint because they are very slow and were not even executed by
> > default with test_btf.
> 
> I think would be good to run them by default.
> The following trivial tweak makes them fast:
> diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c
> index c75fc6447186..589afd4f0e47 100644
> --- a/tools/testing/selftests/bpf/test_btf.c
> +++ b/tools/testing/selftests/bpf/test_btf.c
> @@ -4428,7 +4428,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
> 
>  {
> @@ -4493,7 +4493,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
> 
>  {
> @@ -4564,7 +4564,7 @@ static struct btf_raw_test pprint_test_template[] = {
>         .value_size = sizeof(struct pprint_mapv),
>         .key_type_id = 3,       /* unsigned int */
>         .value_type_id = 16,    /* struct pprint_mapv */
> -       .max_entries = 128 * 1024,
> +       .max_entries = 128,
>  },
> 
> Martin,
> do you remember why you picked such large numbers of entries
> for the test?
It has been a while.  iirc, I think there was no particular reason but
there was only one pprint test and then a few had been added since
then.

> If I read the code correctly smaller number doesn't reduce the test coverage.
Indeed.  Please go ahead to reduce the max_entries.  Sorry for the pain.

> 
> > All the test_btf tests that were moved are modeled as proper sub-tests in
> > test_progs framework for ease of debugging and reporting.
> > 
> > No functional or behavioral changes were intended, I tried to preserve
> > original behavior as close to the original as possible. `test_progs -v` will
> > activate "always_log" flag to emit BTF validation log.
> > 
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> > 
> > v1->v2:
> >  - pretty-print BTF tests were renamed test_btf -> test_btf_pprint, which
> >    allowed GIT to detect that majority of  test_btf code was moved into
> >    prog_tests/btf.c; so diff is much-much smaller;
> 
> Thanks. I hope with addition to pprint test the diff will be even smaller.
> I think it's worth to investigate why they're failing if moved to test_progs.
> I think they're the only tests that exercise seq_read logic.
> Clearly the bug:
> [   25.960993] WARNING: CPU: 2 PID: 1995 at kernel/bpf/hashtab.c:717 htab_map_get_next_key+0x7fc/0xab0
> is still there.
> If pprint tests were part of test_progs we would have caught that earlier.
> 
> Yonghong,
> please take a look at that issue.
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 9a0946ddb705..160c413ee960 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -15,7 +15,7 @@  test_sock
 test_sock_addr
 test_sock_fields
 urandom_read
-test_btf
+test_btf_pprint
 test_sockmap
 test_lirc_mode2_user
 get_cgroup_id_user
diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
similarity index 85%
rename from tools/testing/selftests/bpf/test_btf.c
rename to tools/testing/selftests/bpf/prog_tests/btf.c
index c75fc6447186..94719b692a1b 100644
--- a/tools/testing/selftests/bpf/test_btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -24,40 +24,17 @@ 
 
 #include "bpf_rlimit.h"
 #include "bpf_util.h"
-#include "test_btf.h"
+#include "../test_btf.h"
+#include "test_progs.h"
 
 #define MAX_INSNS	512
 #define MAX_SUBPROGS	16
 
-static uint32_t pass_cnt;
-static uint32_t error_cnt;
-static uint32_t skip_cnt;
+static int duration = 0;
+static bool always_log;
 
-#define CHECK(condition, format...) ({					\
-	int __ret = !!(condition);					\
-	if (__ret) {							\
-		fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__);	\
-		fprintf(stderr, format);				\
-	}								\
-	__ret;								\
-})
-
-static int count_result(int err)
-{
-	if (err)
-		error_cnt++;
-	else
-		pass_cnt++;
-
-	fprintf(stderr, "\n");
-	return err;
-}
-
-static int __base_pr(enum libbpf_print_level level __attribute__((unused)),
-		     const char *format, va_list args)
-{
-	return vfprintf(stderr, format, args);
-}
+#undef CHECK
+#define CHECK(condition, format...) _CHECK(condition, "check", duration, format)
 
 #define BTF_END_RAW 0xdeadbeef
 #define NAME_TBD 0xdeadb33f
@@ -69,21 +46,6 @@  static int __base_pr(enum libbpf_print_level level __attribute__((unused)),
 #define MAX_NR_RAW_U32 1024
 #define BTF_LOG_BUF_SIZE 65535
 
-static struct args {
-	unsigned int raw_test_num;
-	unsigned int file_test_num;
-	unsigned int get_info_test_num;
-	unsigned int info_raw_test_num;
-	unsigned int dedup_test_num;
-	bool raw_test;
-	bool file_test;
-	bool get_info_test;
-	bool pprint_test;
-	bool always_log;
-	bool info_raw_test;
-	bool dedup_test;
-} args;
-
 static char btf_log_buf[BTF_LOG_BUF_SIZE];
 
 static struct btf_header hdr_tmpl = {
@@ -3664,7 +3626,7 @@  static void *btf_raw_create(const struct btf_header *hdr,
 	return raw_btf;
 }
 
-static int do_test_raw(unsigned int test_num)
+static void do_test_raw(unsigned int test_num)
 {
 	struct btf_raw_test *test = &raw_tests[test_num - 1];
 	struct bpf_create_map_attr create_attr = {};
@@ -3674,15 +3636,16 @@  static int do_test_raw(unsigned int test_num)
 	void *raw_btf;
 	int err;
 
-	fprintf(stderr, "BTF raw test[%u] (%s): ", test_num, test->descr);
+	if (!test__start_subtest(test->descr))
+		return;
+
 	raw_btf = btf_raw_create(&hdr_tmpl,
 				 test->raw_types,
 				 test->str_sec,
 				 test->str_sec_size,
 				 &raw_btf_size, NULL);
-
 	if (!raw_btf)
-		return -1;
+		return;
 
 	hdr = raw_btf;
 
@@ -3694,7 +3657,7 @@  static int do_test_raw(unsigned int test_num)
 	*btf_log_buf = '\0';
 	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
 			      btf_log_buf, BTF_LOG_BUF_SIZE,
-			      args.always_log);
+			      always_log);
 	free(raw_btf);
 
 	err = ((btf_fd == -1) != test->btf_load_err);
@@ -3725,32 +3688,12 @@  static int do_test_raw(unsigned int test_num)
 	      map_fd, test->map_create_err);
 
 done:
-	if (!err)
-		fprintf(stderr, "OK");
-
-	if (*btf_log_buf && (err || args.always_log))
+	if (*btf_log_buf && (err || always_log))
 		fprintf(stderr, "\n%s", btf_log_buf);
-
 	if (btf_fd != -1)
 		close(btf_fd);
 	if (map_fd != -1)
 		close(map_fd);
-
-	return err;
-}
-
-static int test_raw(void)
-{
-	unsigned int i;
-	int err = 0;
-
-	if (args.raw_test_num)
-		return count_result(do_test_raw(args.raw_test_num));
-
-	for (i = 1; i <= ARRAY_SIZE(raw_tests); i++)
-		err |= count_result(do_test_raw(i));
-
-	return err;
 }
 
 struct btf_get_info_test {
@@ -3814,11 +3757,6 @@  const struct btf_get_info_test get_info_tests[] = {
 },
 };
 
-static inline __u64 ptr_to_u64(const void *ptr)
-{
-	return (__u64)(unsigned long)ptr;
-}
-
 static int test_big_btf_info(unsigned int test_num)
 {
 	const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
@@ -3851,7 +3789,7 @@  static int test_big_btf_info(unsigned int test_num)
 
 	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
 			      btf_log_buf, BTF_LOG_BUF_SIZE,
-			      args.always_log);
+			      always_log);
 	if (CHECK(btf_fd == -1, "errno:%d", errno)) {
 		err = -1;
 		goto done;
@@ -3892,7 +3830,7 @@  static int test_big_btf_info(unsigned int test_num)
 	fprintf(stderr, "OK");
 
 done:
-	if (*btf_log_buf && (err || args.always_log))
+	if (*btf_log_buf && (err || always_log))
 		fprintf(stderr, "\n%s", btf_log_buf);
 
 	free(raw_btf);
@@ -3939,7 +3877,7 @@  static int test_btf_id(unsigned int test_num)
 
 	btf_fd[0] = bpf_load_btf(raw_btf, raw_btf_size,
 				 btf_log_buf, BTF_LOG_BUF_SIZE,
-				 args.always_log);
+				 always_log);
 	if (CHECK(btf_fd[0] == -1, "errno:%d", errno)) {
 		err = -1;
 		goto done;
@@ -4024,7 +3962,7 @@  static int test_btf_id(unsigned int test_num)
 	fprintf(stderr, "OK");
 
 done:
-	if (*btf_log_buf && (err || args.always_log))
+	if (*btf_log_buf && (err || always_log))
 		fprintf(stderr, "\n%s", btf_log_buf);
 
 	free(raw_btf);
@@ -4039,7 +3977,7 @@  static int test_btf_id(unsigned int test_num)
 	return err;
 }
 
-static int do_test_get_info(unsigned int test_num)
+static void do_test_get_info(unsigned int test_num)
 {
 	const struct btf_get_info_test *test = &get_info_tests[test_num - 1];
 	unsigned int raw_btf_size, user_btf_size, expected_nbytes;
@@ -4048,11 +3986,14 @@  static int do_test_get_info(unsigned int test_num)
 	int btf_fd = -1, err, ret;
 	uint32_t info_len;
 
-	fprintf(stderr, "BTF GET_INFO test[%u] (%s): ",
-		test_num, test->descr);
+	if (!test__start_subtest(test->descr))
+		return;
 
-	if (test->special_test)
-		return test->special_test(test_num);
+	if (test->special_test) {
+		err = test->special_test(test_num);
+		if (CHECK(err, "failed: %d\n", err))
+			return;
+	}
 
 	raw_btf = btf_raw_create(&hdr_tmpl,
 				 test->raw_types,
@@ -4061,7 +4002,7 @@  static int do_test_get_info(unsigned int test_num)
 				 &raw_btf_size, NULL);
 
 	if (!raw_btf)
-		return -1;
+		return;
 
 	*btf_log_buf = '\0';
 
@@ -4073,7 +4014,7 @@  static int do_test_get_info(unsigned int test_num)
 
 	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
 			      btf_log_buf, BTF_LOG_BUF_SIZE,
-			      args.always_log);
+			      always_log);
 	if (CHECK(btf_fd == -1, "errno:%d", errno)) {
 		err = -1;
 		goto done;
@@ -4114,7 +4055,7 @@  static int do_test_get_info(unsigned int test_num)
 	fprintf(stderr, "OK");
 
 done:
-	if (*btf_log_buf && (err || args.always_log))
+	if (*btf_log_buf && (err || always_log))
 		fprintf(stderr, "\n%s", btf_log_buf);
 
 	free(raw_btf);
@@ -4122,22 +4063,6 @@  static int do_test_get_info(unsigned int test_num)
 
 	if (btf_fd != -1)
 		close(btf_fd);
-
-	return err;
-}
-
-static int test_get_info(void)
-{
-	unsigned int i;
-	int err = 0;
-
-	if (args.get_info_test_num)
-		return count_result(do_test_get_info(args.get_info_test_num));
-
-	for (i = 1; i <= ARRAY_SIZE(get_info_tests); i++)
-		err |= count_result(do_test_get_info(i));
-
-	return err;
 }
 
 struct btf_file_test {
@@ -4151,7 +4076,7 @@  static struct btf_file_test file_tests[] = {
 	{ .file = "test_btf_nokv.o", .btf_kv_notfound = true, },
 };
 
-static int do_test_file(unsigned int test_num)
+static void do_test_file(unsigned int test_num)
 {
 	const struct btf_file_test *test = &file_tests[test_num - 1];
 	const char *expected_fnames[] = {"_dummy_tracepoint",
@@ -4169,17 +4094,17 @@  static int do_test_file(unsigned int test_num)
 	struct bpf_map *map;
 	int i, err, prog_fd;
 
-	fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
-		test->file);
+	if (!test__start_subtest(test->file))
+		return;
 
 	btf = btf__parse_elf(test->file, &btf_ext);
 	if (IS_ERR(btf)) {
 		if (PTR_ERR(btf) == -ENOENT) {
-			fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
-			skip_cnt++;
-			return 0;
+			printf("%s:SKIP: No ELF %s found", __func__, BTF_ELF_SEC);
+			test__skip();
+			return;
 		}
-		return PTR_ERR(btf);
+		return;
 	}
 	btf__free(btf);
 
@@ -4188,7 +4113,7 @@  static int do_test_file(unsigned int test_num)
 
 	obj = bpf_object__open(test->file);
 	if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
-		return PTR_ERR(obj);
+		return;
 
 	prog = bpf_program__next(NULL, obj);
 	if (CHECK(!prog, "Cannot find bpf_prog")) {
@@ -4305,715 +4230,11 @@  static int do_test_file(unsigned int test_num)
 	}
 
 skip:
-	fprintf(stderr, "OK");
+	return;
 
 done:
 	free(func_info);
 	bpf_object__close(obj);
-	return err;
-}
-
-static int test_file(void)
-{
-	unsigned int i;
-	int err = 0;
-
-	if (args.file_test_num)
-		return count_result(do_test_file(args.file_test_num));
-
-	for (i = 1; i <= ARRAY_SIZE(file_tests); i++)
-		err |= count_result(do_test_file(i));
-
-	return err;
-}
-
-const char *pprint_enum_str[] = {
-	"ENUM_ZERO",
-	"ENUM_ONE",
-	"ENUM_TWO",
-	"ENUM_THREE",
-};
-
-struct pprint_mapv {
-	uint32_t ui32;
-	uint16_t ui16;
-	/* 2 bytes hole */
-	int32_t si32;
-	uint32_t unused_bits2a:2,
-		bits28:28,
-		unused_bits2b:2;
-	union {
-		uint64_t ui64;
-		uint8_t ui8a[8];
-	};
-	enum {
-		ENUM_ZERO,
-		ENUM_ONE,
-		ENUM_TWO,
-		ENUM_THREE,
-	} aenum;
-	uint32_t ui32b;
-	uint32_t bits2c:2;
-	uint8_t si8_4[2][2];
-};
-
-#ifdef __SIZEOF_INT128__
-struct pprint_mapv_int128 {
-	__int128 si128a;
-	__int128 si128b;
-	unsigned __int128 bits3:3;
-	unsigned __int128 bits80:80;
-	unsigned __int128 ui128;
-};
-#endif
-
-static struct btf_raw_test pprint_test_template[] = {
-{
-	.raw_types = {
-		/* unsighed char */			/* [1] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
-		/* unsigned short */			/* [2] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
-		/* unsigned int */			/* [3] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
-		/* int */				/* [4] */
-		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
-		/* unsigned long long */		/* [5] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
-		/* 2 bits */				/* [6] */
-		BTF_TYPE_INT_ENC(0, 0, 0, 2, 2),
-		/* 28 bits */				/* [7] */
-		BTF_TYPE_INT_ENC(0, 0, 0, 28, 4),
-		/* uint8_t[8] */			/* [8] */
-		BTF_TYPE_ARRAY_ENC(9, 1, 8),
-		/* typedef unsigned char uint8_t */	/* [9] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 1),
-		/* typedef unsigned short uint16_t */	/* [10] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 2),
-		/* typedef unsigned int uint32_t */	/* [11] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 3),
-		/* typedef int int32_t */		/* [12] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 4),
-		/* typedef unsigned long long uint64_t *//* [13] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 5),
-		/* union (anon) */			/* [14] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
-		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
-		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
-		/* enum (anon) */			/* [15] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
-		BTF_ENUM_ENC(NAME_TBD, 0),
-		BTF_ENUM_ENC(NAME_TBD, 1),
-		BTF_ENUM_ENC(NAME_TBD, 2),
-		BTF_ENUM_ENC(NAME_TBD, 3),
-		/* struct pprint_mapv */		/* [16] */
-		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 11), 40),
-		BTF_MEMBER_ENC(NAME_TBD, 11, 0),	/* uint32_t ui32 */
-		BTF_MEMBER_ENC(NAME_TBD, 10, 32),	/* uint16_t ui16 */
-		BTF_MEMBER_ENC(NAME_TBD, 12, 64),	/* int32_t si32 */
-		BTF_MEMBER_ENC(NAME_TBD, 6, 96),	/* unused_bits2a */
-		BTF_MEMBER_ENC(NAME_TBD, 7, 98),	/* bits28 */
-		BTF_MEMBER_ENC(NAME_TBD, 6, 126),	/* unused_bits2b */
-		BTF_MEMBER_ENC(0, 14, 128),		/* union (anon) */
-		BTF_MEMBER_ENC(NAME_TBD, 15, 192),	/* aenum */
-		BTF_MEMBER_ENC(NAME_TBD, 11, 224),	/* uint32_t ui32b */
-		BTF_MEMBER_ENC(NAME_TBD, 6, 256),	/* bits2c */
-		BTF_MEMBER_ENC(NAME_TBD, 17, 264),	/* si8_4 */
-		BTF_TYPE_ARRAY_ENC(18, 1, 2),		/* [17] */
-		BTF_TYPE_ARRAY_ENC(1, 1, 2),		/* [18] */
-		BTF_END_RAW,
-	},
-	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0si8_4"),
-	.key_size = sizeof(unsigned int),
-	.value_size = sizeof(struct pprint_mapv),
-	.key_type_id = 3,	/* unsigned int */
-	.value_type_id = 16,	/* struct pprint_mapv */
-	.max_entries = 128 * 1024,
-},
-
-{
-	/* this type will have the same type as the
-	 * first .raw_types definition, but struct type will
-	 * be encoded with kind_flag set.
-	 */
-	.raw_types = {
-		/* unsighed char */			/* [1] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
-		/* unsigned short */			/* [2] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
-		/* unsigned int */			/* [3] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
-		/* int */				/* [4] */
-		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
-		/* unsigned long long */		/* [5] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
-		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [6] */
-		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [7] */
-		/* uint8_t[8] */			/* [8] */
-		BTF_TYPE_ARRAY_ENC(9, 1, 8),
-		/* typedef unsigned char uint8_t */	/* [9] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 1),
-		/* typedef unsigned short uint16_t */	/* [10] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 2),
-		/* typedef unsigned int uint32_t */	/* [11] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 3),
-		/* typedef int int32_t */		/* [12] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 4),
-		/* typedef unsigned long long uint64_t *//* [13] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 5),
-		/* union (anon) */			/* [14] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
-		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
-		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
-		/* enum (anon) */			/* [15] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
-		BTF_ENUM_ENC(NAME_TBD, 0),
-		BTF_ENUM_ENC(NAME_TBD, 1),
-		BTF_ENUM_ENC(NAME_TBD, 2),
-		BTF_ENUM_ENC(NAME_TBD, 3),
-		/* struct pprint_mapv */		/* [16] */
-		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 11), 40),
-		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),	/* uint32_t ui32 */
-		BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)),	/* uint16_t ui16 */
-		BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)),	/* int32_t si32 */
-		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 96)),	/* unused_bits2a */
-		BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)),	/* bits28 */
-		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 126)),	/* unused_bits2b */
-		BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),	/* union (anon) */
-		BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),	/* aenum */
-		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 224)),	/* uint32_t ui32b */
-		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 256)),	/* bits2c */
-		BTF_MEMBER_ENC(NAME_TBD, 17, 264),	/* si8_4 */
-		BTF_TYPE_ARRAY_ENC(18, 1, 2),		/* [17] */
-		BTF_TYPE_ARRAY_ENC(1, 1, 2),		/* [18] */
-		BTF_END_RAW,
-	},
-	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0si8_4"),
-	.key_size = sizeof(unsigned int),
-	.value_size = sizeof(struct pprint_mapv),
-	.key_type_id = 3,	/* unsigned int */
-	.value_type_id = 16,	/* struct pprint_mapv */
-	.max_entries = 128 * 1024,
-},
-
-{
-	/* this type will have the same layout as the
-	 * first .raw_types definition. The struct type will
-	 * be encoded with kind_flag set, bitfield members
-	 * are added typedef/const/volatile, and bitfield members
-	 * will have both int and enum types.
-	 */
-	.raw_types = {
-		/* unsighed char */			/* [1] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
-		/* unsigned short */			/* [2] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
-		/* unsigned int */			/* [3] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
-		/* int */				/* [4] */
-		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
-		/* unsigned long long */		/* [5] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
-		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [6] */
-		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [7] */
-		/* uint8_t[8] */			/* [8] */
-		BTF_TYPE_ARRAY_ENC(9, 1, 8),
-		/* typedef unsigned char uint8_t */	/* [9] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 1),
-		/* typedef unsigned short uint16_t */	/* [10] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 2),
-		/* typedef unsigned int uint32_t */	/* [11] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 3),
-		/* typedef int int32_t */		/* [12] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 4),
-		/* typedef unsigned long long uint64_t *//* [13] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 5),
-		/* union (anon) */			/* [14] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
-		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
-		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
-		/* enum (anon) */			/* [15] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
-		BTF_ENUM_ENC(NAME_TBD, 0),
-		BTF_ENUM_ENC(NAME_TBD, 1),
-		BTF_ENUM_ENC(NAME_TBD, 2),
-		BTF_ENUM_ENC(NAME_TBD, 3),
-		/* struct pprint_mapv */		/* [16] */
-		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 11), 40),
-		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),	/* uint32_t ui32 */
-		BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)),	/* uint16_t ui16 */
-		BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)),	/* int32_t si32 */
-		BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 96)),	/* unused_bits2a */
-		BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)),	/* bits28 */
-		BTF_MEMBER_ENC(NAME_TBD, 19, BTF_MEMBER_OFFSET(2, 126)),/* unused_bits2b */
-		BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),	/* union (anon) */
-		BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),	/* aenum */
-		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 224)),	/* uint32_t ui32b */
-		BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 256)),	/* bits2c */
-		BTF_MEMBER_ENC(NAME_TBD, 20, BTF_MEMBER_OFFSET(0, 264)),	/* si8_4 */
-		/* typedef unsigned int ___int */	/* [17] */
-		BTF_TYPEDEF_ENC(NAME_TBD, 18),
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), 6),	/* [18] */
-		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 15),	/* [19] */
-		BTF_TYPE_ARRAY_ENC(21, 1, 2),					/* [20] */
-		BTF_TYPE_ARRAY_ENC(1, 1, 2),					/* [21] */
-		BTF_END_RAW,
-	},
-	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0___int\0si8_4"),
-	.key_size = sizeof(unsigned int),
-	.value_size = sizeof(struct pprint_mapv),
-	.key_type_id = 3,	/* unsigned int */
-	.value_type_id = 16,	/* struct pprint_mapv */
-	.max_entries = 128 * 1024,
-},
-
-#ifdef __SIZEOF_INT128__
-{
-	/* test int128 */
-	.raw_types = {
-		/* unsigned int */				/* [1] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
-		/* __int128 */					/* [2] */
-		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 128, 16),
-		/* unsigned __int128 */				/* [3] */
-		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 128, 16),
-		/* struct pprint_mapv_int128 */			/* [4] */
-		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 5), 64),
-		BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)),		/* si128a */
-		BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 128)),		/* si128b */
-		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(3, 256)),		/* bits3 */
-		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(80, 259)),	/* bits80 */
-		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(0, 384)),		/* ui128 */
-		BTF_END_RAW,
-	},
-	BTF_STR_SEC("\0unsigned int\0__int128\0unsigned __int128\0pprint_mapv_int128\0si128a\0si128b\0bits3\0bits80\0ui128"),
-	.key_size = sizeof(unsigned int),
-	.value_size = sizeof(struct pprint_mapv_int128),
-	.key_type_id = 1,
-	.value_type_id = 4,
-	.max_entries = 128 * 1024,
-	.mapv_kind = PPRINT_MAPV_KIND_INT128,
-},
-#endif
-
-};
-
-static struct btf_pprint_test_meta {
-	const char *descr;
-	enum bpf_map_type map_type;
-	const char *map_name;
-	bool ordered_map;
-	bool lossless_map;
-	bool percpu_map;
-} pprint_tests_meta[] = {
-{
-	.descr = "BTF pretty print array",
-	.map_type = BPF_MAP_TYPE_ARRAY,
-	.map_name = "pprint_test_array",
-	.ordered_map = true,
-	.lossless_map = true,
-	.percpu_map = false,
-},
-
-{
-	.descr = "BTF pretty print hash",
-	.map_type = BPF_MAP_TYPE_HASH,
-	.map_name = "pprint_test_hash",
-	.ordered_map = false,
-	.lossless_map = true,
-	.percpu_map = false,
-},
-
-{
-	.descr = "BTF pretty print lru hash",
-	.map_type = BPF_MAP_TYPE_LRU_HASH,
-	.map_name = "pprint_test_lru_hash",
-	.ordered_map = false,
-	.lossless_map = false,
-	.percpu_map = false,
-},
-
-{
-	.descr = "BTF pretty print percpu array",
-	.map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.map_name = "pprint_test_percpu_array",
-	.ordered_map = true,
-	.lossless_map = true,
-	.percpu_map = true,
-},
-
-{
-	.descr = "BTF pretty print percpu hash",
-	.map_type = BPF_MAP_TYPE_PERCPU_HASH,
-	.map_name = "pprint_test_percpu_hash",
-	.ordered_map = false,
-	.lossless_map = true,
-	.percpu_map = true,
-},
-
-{
-	.descr = "BTF pretty print lru percpu hash",
-	.map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
-	.map_name = "pprint_test_lru_percpu_hash",
-	.ordered_map = false,
-	.lossless_map = false,
-	.percpu_map = true,
-},
-
-};
-
-static size_t get_pprint_mapv_size(enum pprint_mapv_kind_t mapv_kind)
-{
-	if (mapv_kind == PPRINT_MAPV_KIND_BASIC)
-		return sizeof(struct pprint_mapv);
-
-#ifdef __SIZEOF_INT128__
-	if (mapv_kind == PPRINT_MAPV_KIND_INT128)
-		return sizeof(struct pprint_mapv_int128);
-#endif
-
-	assert(0);
-}
-
-static void set_pprint_mapv(enum pprint_mapv_kind_t mapv_kind,
-			    void *mapv, uint32_t i,
-			    int num_cpus, int rounded_value_size)
-{
-	int cpu;
-
-	if (mapv_kind == PPRINT_MAPV_KIND_BASIC) {
-		struct pprint_mapv *v = mapv;
-
-		for (cpu = 0; cpu < num_cpus; cpu++) {
-			v->ui32 = i + cpu;
-			v->si32 = -i;
-			v->unused_bits2a = 3;
-			v->bits28 = i;
-			v->unused_bits2b = 3;
-			v->ui64 = i;
-			v->aenum = i & 0x03;
-			v->ui32b = 4;
-			v->bits2c = 1;
-			v->si8_4[0][0] = (cpu + i) & 0xff;
-			v->si8_4[0][1] = (cpu + i + 1) & 0xff;
-			v->si8_4[1][0] = (cpu + i + 2) & 0xff;
-			v->si8_4[1][1] = (cpu + i + 3) & 0xff;
-			v = (void *)v + rounded_value_size;
-		}
-	}
-
-#ifdef __SIZEOF_INT128__
-	if (mapv_kind == PPRINT_MAPV_KIND_INT128) {
-		struct pprint_mapv_int128 *v = mapv;
-
-		for (cpu = 0; cpu < num_cpus; cpu++) {
-			v->si128a = i;
-			v->si128b = -i;
-			v->bits3 = i & 0x07;
-			v->bits80 = (((unsigned __int128)1) << 64) + i;
-			v->ui128 = (((unsigned __int128)2) << 64) + i;
-			v = (void *)v + rounded_value_size;
-		}
-	}
-#endif
-}
-
-ssize_t get_pprint_expected_line(enum pprint_mapv_kind_t mapv_kind,
-				 char *expected_line, ssize_t line_size,
-				 bool percpu_map, unsigned int next_key,
-				 int cpu, void *mapv)
-{
-	ssize_t nexpected_line = -1;
-
-	if (mapv_kind == PPRINT_MAPV_KIND_BASIC) {
-		struct pprint_mapv *v = mapv;
-
-		nexpected_line = snprintf(expected_line, line_size,
-					  "%s%u: {%u,0,%d,0x%x,0x%x,0x%x,"
-					  "{%llu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s,"
-					  "%u,0x%x,[[%d,%d],[%d,%d]]}\n",
-					  percpu_map ? "\tcpu" : "",
-					  percpu_map ? cpu : next_key,
-					  v->ui32, v->si32,
-					  v->unused_bits2a,
-					  v->bits28,
-					  v->unused_bits2b,
-					  (__u64)v->ui64,
-					  v->ui8a[0], v->ui8a[1],
-					  v->ui8a[2], v->ui8a[3],
-					  v->ui8a[4], v->ui8a[5],
-					  v->ui8a[6], v->ui8a[7],
-					  pprint_enum_str[v->aenum],
-					  v->ui32b,
-					  v->bits2c,
-					  v->si8_4[0][0], v->si8_4[0][1],
-					  v->si8_4[1][0], v->si8_4[1][1]);
-	}
-
-#ifdef __SIZEOF_INT128__
-	if (mapv_kind == PPRINT_MAPV_KIND_INT128) {
-		struct pprint_mapv_int128 *v = mapv;
-
-		nexpected_line = snprintf(expected_line, line_size,
-					  "%s%u: {0x%lx,0x%lx,0x%lx,"
-					  "0x%lx%016lx,0x%lx%016lx}\n",
-					  percpu_map ? "\tcpu" : "",
-					  percpu_map ? cpu : next_key,
-					  (uint64_t)v->si128a,
-					  (uint64_t)v->si128b,
-					  (uint64_t)v->bits3,
-					  (uint64_t)(v->bits80 >> 64),
-					  (uint64_t)v->bits80,
-					  (uint64_t)(v->ui128 >> 64),
-					  (uint64_t)v->ui128);
-	}
-#endif
-
-	return nexpected_line;
-}
-
-static int check_line(const char *expected_line, int nexpected_line,
-		      int expected_line_len, const char *line)
-{
-	if (CHECK(nexpected_line == expected_line_len,
-		  "expected_line is too long"))
-		return -1;
-
-	if (strcmp(expected_line, line)) {
-		fprintf(stderr, "unexpected pprint output\n");
-		fprintf(stderr, "expected: %s", expected_line);
-		fprintf(stderr, "    read: %s", line);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int do_test_pprint(int test_num)
-{
-	const struct btf_raw_test *test = &pprint_test_template[test_num];
-	enum pprint_mapv_kind_t mapv_kind = test->mapv_kind;
-	struct bpf_create_map_attr create_attr = {};
-	bool ordered_map, lossless_map, percpu_map;
-	int err, ret, num_cpus, rounded_value_size;
-	unsigned int key, nr_read_elems;
-	int map_fd = -1, btf_fd = -1;
-	unsigned int raw_btf_size;
-	char expected_line[255];
-	FILE *pin_file = NULL;
-	char pin_path[255];
-	size_t line_len = 0;
-	char *line = NULL;
-	void *mapv = NULL;
-	uint8_t *raw_btf;
-	ssize_t nread;
-
-	fprintf(stderr, "%s(#%d)......", test->descr, test_num);
-	raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
-				 test->str_sec, test->str_sec_size,
-				 &raw_btf_size, NULL);
-
-	if (!raw_btf)
-		return -1;
-
-	*btf_log_buf = '\0';
-	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
-			      btf_log_buf, BTF_LOG_BUF_SIZE,
-			      args.always_log);
-	free(raw_btf);
-
-	if (CHECK(btf_fd == -1, "errno:%d", errno)) {
-		err = -1;
-		goto done;
-	}
-
-	create_attr.name = test->map_name;
-	create_attr.map_type = test->map_type;
-	create_attr.key_size = test->key_size;
-	create_attr.value_size = test->value_size;
-	create_attr.max_entries = test->max_entries;
-	create_attr.btf_fd = btf_fd;
-	create_attr.btf_key_type_id = test->key_type_id;
-	create_attr.btf_value_type_id = test->value_type_id;
-
-	map_fd = bpf_create_map_xattr(&create_attr);
-	if (CHECK(map_fd == -1, "errno:%d", errno)) {
-		err = -1;
-		goto done;
-	}
-
-	ret = snprintf(pin_path, sizeof(pin_path), "%s/%s",
-		       "/sys/fs/bpf", test->map_name);
-
-	if (CHECK(ret == sizeof(pin_path), "pin_path %s/%s is too long",
-		  "/sys/fs/bpf", test->map_name)) {
-		err = -1;
-		goto done;
-	}
-
-	err = bpf_obj_pin(map_fd, pin_path);
-	if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
-		goto done;
-
-	percpu_map = test->percpu_map;
-	num_cpus = percpu_map ? bpf_num_possible_cpus() : 1;
-	rounded_value_size = round_up(get_pprint_mapv_size(mapv_kind), 8);
-	mapv = calloc(num_cpus, rounded_value_size);
-	if (CHECK(!mapv, "mapv allocation failure")) {
-		err = -1;
-		goto done;
-	}
-
-	for (key = 0; key < test->max_entries; key++) {
-		set_pprint_mapv(mapv_kind, mapv, key, num_cpus, rounded_value_size);
-		bpf_map_update_elem(map_fd, &key, mapv, 0);
-	}
-
-	pin_file = fopen(pin_path, "r");
-	if (CHECK(!pin_file, "fopen(%s): errno:%d", pin_path, errno)) {
-		err = -1;
-		goto done;
-	}
-
-	/* Skip lines start with '#' */
-	while ((nread = getline(&line, &line_len, pin_file)) > 0 &&
-	       *line == '#')
-		;
-
-	if (CHECK(nread <= 0, "Unexpected EOF")) {
-		err = -1;
-		goto done;
-	}
-
-	nr_read_elems = 0;
-	ordered_map = test->ordered_map;
-	lossless_map = test->lossless_map;
-	do {
-		ssize_t nexpected_line;
-		unsigned int next_key;
-		void *cmapv;
-		int cpu;
-
-		next_key = ordered_map ? nr_read_elems : atoi(line);
-		set_pprint_mapv(mapv_kind, mapv, next_key, num_cpus, rounded_value_size);
-		cmapv = mapv;
-
-		for (cpu = 0; cpu < num_cpus; cpu++) {
-			if (percpu_map) {
-				/* for percpu map, the format looks like:
-				 * <key>: {
-				 *	cpu0: <value_on_cpu0>
-				 *	cpu1: <value_on_cpu1>
-				 *	...
-				 *	cpun: <value_on_cpun>
-				 * }
-				 *
-				 * let us verify the line containing the key here.
-				 */
-				if (cpu == 0) {
-					nexpected_line = snprintf(expected_line,
-								  sizeof(expected_line),
-								  "%u: {\n",
-								  next_key);
-
-					err = check_line(expected_line, nexpected_line,
-							 sizeof(expected_line), line);
-					if (err == -1)
-						goto done;
-				}
-
-				/* read value@cpu */
-				nread = getline(&line, &line_len, pin_file);
-				if (nread < 0)
-					break;
-			}
-
-			nexpected_line = get_pprint_expected_line(mapv_kind, expected_line,
-								  sizeof(expected_line),
-								  percpu_map, next_key,
-								  cpu, cmapv);
-			err = check_line(expected_line, nexpected_line,
-					 sizeof(expected_line), line);
-			if (err == -1)
-				goto done;
-
-			cmapv = cmapv + rounded_value_size;
-		}
-
-		if (percpu_map) {
-			/* skip the last bracket for the percpu map */
-			nread = getline(&line, &line_len, pin_file);
-			if (nread < 0)
-				break;
-		}
-
-		nread = getline(&line, &line_len, pin_file);
-	} while (++nr_read_elems < test->max_entries && nread > 0);
-
-	if (lossless_map &&
-	    CHECK(nr_read_elems < test->max_entries,
-		  "Unexpected EOF. nr_read_elems:%u test->max_entries:%u",
-		  nr_read_elems, test->max_entries)) {
-		err = -1;
-		goto done;
-	}
-
-	if (CHECK(nread > 0, "Unexpected extra pprint output: %s", line)) {
-		err = -1;
-		goto done;
-	}
-
-	err = 0;
-
-done:
-	if (mapv)
-		free(mapv);
-	if (!err)
-		fprintf(stderr, "OK");
-	if (*btf_log_buf && (err || args.always_log))
-		fprintf(stderr, "\n%s", btf_log_buf);
-	if (btf_fd != -1)
-		close(btf_fd);
-	if (map_fd != -1)
-		close(map_fd);
-	if (pin_file)
-		fclose(pin_file);
-	unlink(pin_path);
-	free(line);
-
-	return err;
-}
-
-static int test_pprint(void)
-{
-	unsigned int i;
-	int err = 0;
-
-	/* test various maps with the first test template */
-	for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) {
-		pprint_test_template[0].descr = pprint_tests_meta[i].descr;
-		pprint_test_template[0].map_type = pprint_tests_meta[i].map_type;
-		pprint_test_template[0].map_name = pprint_tests_meta[i].map_name;
-		pprint_test_template[0].ordered_map = pprint_tests_meta[i].ordered_map;
-		pprint_test_template[0].lossless_map = pprint_tests_meta[i].lossless_map;
-		pprint_test_template[0].percpu_map = pprint_tests_meta[i].percpu_map;
-
-		err |= count_result(do_test_pprint(0));
-	}
-
-	/* test rest test templates with the first map */
-	for (i = 1; i < ARRAY_SIZE(pprint_test_template); i++) {
-		pprint_test_template[i].descr = pprint_tests_meta[0].descr;
-		pprint_test_template[i].map_type = pprint_tests_meta[0].map_type;
-		pprint_test_template[i].map_name = pprint_tests_meta[0].map_name;
-		pprint_test_template[i].ordered_map = pprint_tests_meta[0].ordered_map;
-		pprint_test_template[i].lossless_map = pprint_tests_meta[0].lossless_map;
-		pprint_test_template[i].percpu_map = pprint_tests_meta[0].percpu_map;
-		err |= count_result(do_test_pprint(i));
-	}
-
-	return err;
 }
 
 #define BPF_LINE_INFO_ENC(insn_off, file_off, line_off, line_num, line_col) \
@@ -6178,7 +5399,7 @@  static int test_get_linfo(const struct prog_info_raw_test *test,
 	return err;
 }
 
-static int do_test_info_raw(unsigned int test_num)
+static void do_test_info_raw(unsigned int test_num)
 {
 	const struct prog_info_raw_test *test = &info_raw_tests[test_num - 1];
 	unsigned int raw_btf_size, linfo_str_off, linfo_size;
@@ -6187,18 +5408,19 @@  static int do_test_info_raw(unsigned int test_num)
 	const char *ret_next_str;
 	union bpf_attr attr = {};
 
-	fprintf(stderr, "BTF prog info raw test[%u] (%s): ", test_num, test->descr);
+	if (!test__start_subtest(test->descr))
+		return;
+
 	raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
 				 test->str_sec, test->str_sec_size,
 				 &raw_btf_size, &ret_next_str);
-
 	if (!raw_btf)
-		return -1;
+		return;
 
 	*btf_log_buf = '\0';
 	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
 			      btf_log_buf, BTF_LOG_BUF_SIZE,
-			      args.always_log);
+			      always_log);
 	free(raw_btf);
 
 	if (CHECK(btf_fd == -1, "invalid btf_fd errno:%d", errno)) {
@@ -6206,7 +5428,7 @@  static int do_test_info_raw(unsigned int test_num)
 		goto done;
 	}
 
-	if (*btf_log_buf && args.always_log)
+	if (*btf_log_buf && always_log)
 		fprintf(stderr, "\n%s", btf_log_buf);
 	*btf_log_buf = '\0';
 
@@ -6261,10 +5483,7 @@  static int do_test_info_raw(unsigned int test_num)
 		goto done;
 
 done:
-	if (!err)
-		fprintf(stderr, "OK");
-
-	if (*btf_log_buf && (err || args.always_log))
+	if (*btf_log_buf && (err || always_log))
 		fprintf(stderr, "\n%s", btf_log_buf);
 
 	if (btf_fd != -1)
@@ -6274,22 +5493,6 @@  static int do_test_info_raw(unsigned int test_num)
 
 	if (!IS_ERR(patched_linfo))
 		free(patched_linfo);
-
-	return err;
-}
-
-static int test_info_raw(void)
-{
-	unsigned int i;
-	int err = 0;
-
-	if (args.info_raw_test_num)
-		return count_result(do_test_info_raw(args.info_raw_test_num));
-
-	for (i = 1; i <= ARRAY_SIZE(info_raw_tests); i++)
-		err |= count_result(do_test_info_raw(i));
-
-	return err;
 }
 
 struct btf_raw_data {
@@ -6754,7 +5957,7 @@  static void dump_btf_strings(const char *strs, __u32 len)
 	}
 }
 
-static int do_test_dedup(unsigned int test_num)
+static void do_test_dedup(unsigned int test_num)
 {
 	const struct btf_dedup_test *test = &dedup_tests[test_num - 1];
 	__u32 test_nr_types, expect_nr_types, test_btf_size, expect_btf_size;
@@ -6769,13 +5972,15 @@  static int do_test_dedup(unsigned int test_num)
 	void *raw_btf;
 	int err = 0, i;
 
-	fprintf(stderr, "BTF dedup test[%u] (%s):", test_num, test->descr);
+	if (!test__start_subtest(test->descr))
+		return;
 
 	raw_btf = btf_raw_create(&hdr_tmpl, test->input.raw_types,
 				 test->input.str_sec, test->input.str_sec_size,
 				 &raw_btf_size, &ret_test_next_str);
 	if (!raw_btf)
-		return -1;
+		return;
+
 	test_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
 	free(raw_btf);
 	if (CHECK(IS_ERR(test_btf), "invalid test_btf errno:%ld",
@@ -6789,7 +5994,7 @@  static int do_test_dedup(unsigned int test_num)
 				 test->expect.str_sec_size,
 				 &raw_btf_size, &ret_expect_next_str);
 	if (!raw_btf)
-		return -1;
+		return;
 	expect_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
 	free(raw_btf);
 	if (CHECK(IS_ERR(expect_btf), "invalid expect_btf errno:%ld",
@@ -6894,174 +6099,26 @@  static int do_test_dedup(unsigned int test_num)
 	}
 
 done:
-	if (!err)
-		fprintf(stderr, "OK");
 	if (!IS_ERR(test_btf))
 		btf__free(test_btf);
 	if (!IS_ERR(expect_btf))
 		btf__free(expect_btf);
-
-	return err;
 }
 
-static int test_dedup(void)
+void test_btf(void)
 {
-	unsigned int i;
-	int err = 0;
+	int i;
 
-	if (args.dedup_test_num)
-		return count_result(do_test_dedup(args.dedup_test_num));
+	always_log = env.verbosity > VERBOSE_NONE;
 
+	for (i = 1; i <= ARRAY_SIZE(raw_tests); i++)
+		do_test_raw(i);
+	for (i = 1; i <= ARRAY_SIZE(get_info_tests); i++)
+		do_test_get_info(i);
+	for (i = 1; i <= ARRAY_SIZE(file_tests); i++)
+		do_test_file(i);
+	for (i = 1; i <= ARRAY_SIZE(info_raw_tests); i++)
+		do_test_info_raw(i);
 	for (i = 1; i <= ARRAY_SIZE(dedup_tests); i++)
-		err |= count_result(do_test_dedup(i));
-
-	return err;
-}
-
-static void usage(const char *cmd)
-{
-	fprintf(stderr, "Usage: %s [-l] [[-r btf_raw_test_num (1 - %zu)] |\n"
-			"\t[-g btf_get_info_test_num (1 - %zu)] |\n"
-			"\t[-f btf_file_test_num (1 - %zu)] |\n"
-			"\t[-k btf_prog_info_raw_test_num (1 - %zu)] |\n"
-			"\t[-p (pretty print test)] |\n"
-			"\t[-d btf_dedup_test_num (1 - %zu)]]\n",
-		cmd, ARRAY_SIZE(raw_tests), ARRAY_SIZE(get_info_tests),
-		ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests),
-		ARRAY_SIZE(dedup_tests));
-}
-
-static int parse_args(int argc, char **argv)
-{
-	const char *optstr = "hlpk:f:r:g:d:";
-	int opt;
-
-	while ((opt = getopt(argc, argv, optstr)) != -1) {
-		switch (opt) {
-		case 'l':
-			args.always_log = true;
-			break;
-		case 'f':
-			args.file_test_num = atoi(optarg);
-			args.file_test = true;
-			break;
-		case 'r':
-			args.raw_test_num = atoi(optarg);
-			args.raw_test = true;
-			break;
-		case 'g':
-			args.get_info_test_num = atoi(optarg);
-			args.get_info_test = true;
-			break;
-		case 'p':
-			args.pprint_test = true;
-			break;
-		case 'k':
-			args.info_raw_test_num = atoi(optarg);
-			args.info_raw_test = true;
-			break;
-		case 'd':
-			args.dedup_test_num = atoi(optarg);
-			args.dedup_test = true;
-			break;
-		case 'h':
-			usage(argv[0]);
-			exit(0);
-		default:
-			usage(argv[0]);
-			return -1;
-		}
-	}
-
-	if (args.raw_test_num &&
-	    (args.raw_test_num < 1 ||
-	     args.raw_test_num > ARRAY_SIZE(raw_tests))) {
-		fprintf(stderr, "BTF raw test number must be [1 - %zu]\n",
-			ARRAY_SIZE(raw_tests));
-		return -1;
-	}
-
-	if (args.file_test_num &&
-	    (args.file_test_num < 1 ||
-	     args.file_test_num > ARRAY_SIZE(file_tests))) {
-		fprintf(stderr, "BTF file test number must be [1 - %zu]\n",
-			ARRAY_SIZE(file_tests));
-		return -1;
-	}
-
-	if (args.get_info_test_num &&
-	    (args.get_info_test_num < 1 ||
-	     args.get_info_test_num > ARRAY_SIZE(get_info_tests))) {
-		fprintf(stderr, "BTF get info test number must be [1 - %zu]\n",
-			ARRAY_SIZE(get_info_tests));
-		return -1;
-	}
-
-	if (args.info_raw_test_num &&
-	    (args.info_raw_test_num < 1 ||
-	     args.info_raw_test_num > ARRAY_SIZE(info_raw_tests))) {
-		fprintf(stderr, "BTF prog info raw test number must be [1 - %zu]\n",
-			ARRAY_SIZE(info_raw_tests));
-		return -1;
-	}
-
-	if (args.dedup_test_num &&
-	    (args.dedup_test_num < 1 ||
-	     args.dedup_test_num > ARRAY_SIZE(dedup_tests))) {
-		fprintf(stderr, "BTF dedup test number must be [1 - %zu]\n",
-			ARRAY_SIZE(dedup_tests));
-		return -1;
-	}
-
-	return 0;
-}
-
-static void print_summary(void)
-{
-	fprintf(stderr, "PASS:%u SKIP:%u FAIL:%u\n",
-		pass_cnt - skip_cnt, skip_cnt, error_cnt);
-}
-
-int main(int argc, char **argv)
-{
-	int err = 0;
-
-	err = parse_args(argc, argv);
-	if (err)
-		return err;
-
-	if (args.always_log)
-		libbpf_set_print(__base_pr);
-
-	if (args.raw_test)
-		err |= test_raw();
-
-	if (args.get_info_test)
-		err |= test_get_info();
-
-	if (args.file_test)
-		err |= test_file();
-
-	if (args.pprint_test)
-		err |= test_pprint();
-
-	if (args.info_raw_test)
-		err |= test_info_raw();
-
-	if (args.dedup_test)
-		err |= test_dedup();
-
-	if (args.raw_test || args.get_info_test || args.file_test ||
-	    args.pprint_test || args.info_raw_test || args.dedup_test)
-		goto done;
-
-	err |= test_raw();
-	err |= test_get_info();
-	err |= test_file();
-	err |= test_info_raw();
-	err |= test_dedup();
-
-done:
-	print_summary();
-	return err;
+		do_test_dedup(i);
 }
diff --git a/tools/testing/selftests/bpf/test_btf_pprint.c b/tools/testing/selftests/bpf/test_btf_pprint.c
new file mode 100644
index 000000000000..2108c1ada14b
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_btf_pprint.c
@@ -0,0 +1,969 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Facebook */
+
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/filter.h>
+#include <linux/unistd.h>
+#include <bpf/bpf.h>
+#include <sys/resource.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <bpf/libbpf.h>
+#include <bpf/btf.h>
+
+#include "bpf_rlimit.h"
+#include "bpf_util.h"
+#include "test_btf.h"
+
+#define MAX_INSNS	512
+#define MAX_SUBPROGS	16
+
+static uint32_t pass_cnt;
+static uint32_t error_cnt;
+static uint32_t skip_cnt;
+
+#define CHECK(condition, format...) ({					\
+	int __ret = !!(condition);					\
+	if (__ret) {							\
+		fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__);	\
+		fprintf(stderr, format);				\
+	}								\
+	__ret;								\
+})
+
+static int count_result(int err)
+{
+	if (err)
+		error_cnt++;
+	else
+		pass_cnt++;
+
+	fprintf(stderr, "\n");
+	return err;
+}
+
+static int __base_pr(enum libbpf_print_level level __attribute__((unused)),
+		     const char *format, va_list args)
+{
+	return vfprintf(stderr, format, args);
+}
+
+#define BTF_END_RAW 0xdeadbeef
+#define NAME_TBD 0xdeadb33f
+
+#define NAME_NTH(N) (0xffff0000 | N)
+#define IS_NAME_NTH(X) ((X & 0xffff0000) == 0xffff0000)
+#define GET_NAME_NTH_IDX(X) (X & 0x0000ffff)
+
+#define MAX_NR_RAW_U32 1024
+#define BTF_LOG_BUF_SIZE 65535
+
+static struct args {
+	bool always_log;
+} args;
+
+static char btf_log_buf[BTF_LOG_BUF_SIZE];
+
+static struct btf_header hdr_tmpl = {
+	.magic = BTF_MAGIC,
+	.version = BTF_VERSION,
+	.hdr_len = sizeof(struct btf_header),
+};
+
+/* several different mapv kinds(types) supported by pprint */
+enum pprint_mapv_kind_t {
+	PPRINT_MAPV_KIND_BASIC = 0,
+	PPRINT_MAPV_KIND_INT128,
+};
+
+struct btf_raw_test {
+	const char *descr;
+	const char *str_sec;
+	const char *map_name;
+	const char *err_str;
+	__u32 raw_types[MAX_NR_RAW_U32];
+	__u32 str_sec_size;
+	enum bpf_map_type map_type;
+	__u32 key_size;
+	__u32 value_size;
+	__u32 key_type_id;
+	__u32 value_type_id;
+	__u32 max_entries;
+	bool btf_load_err;
+	bool map_create_err;
+	bool ordered_map;
+	bool lossless_map;
+	bool percpu_map;
+	int hdr_len_delta;
+	int type_off_delta;
+	int str_off_delta;
+	int str_len_delta;
+	enum pprint_mapv_kind_t mapv_kind;
+};
+
+#define BTF_STR_SEC(str) \
+	.str_sec = str, .str_sec_size = sizeof(str)
+
+static const char *get_next_str(const char *start, const char *end)
+{
+	return start < end - 1 ? start + 1 : NULL;
+}
+
+static int get_raw_sec_size(const __u32 *raw_types)
+{
+	int i;
+
+	for (i = MAX_NR_RAW_U32 - 1;
+	     i >= 0 && raw_types[i] != BTF_END_RAW;
+	     i--)
+		;
+
+	return i < 0 ? i : i * sizeof(raw_types[0]);
+}
+
+static void *btf_raw_create(const struct btf_header *hdr,
+			    const __u32 *raw_types,
+			    const char *str,
+			    unsigned int str_sec_size,
+			    unsigned int *btf_size,
+			    const char **ret_next_str)
+{
+	const char *next_str = str, *end_str = str + str_sec_size;
+	const char **strs_idx = NULL, **tmp_strs_idx;
+	int strs_cap = 0, strs_cnt = 0, next_str_idx = 0;
+	unsigned int size_needed, offset;
+	struct btf_header *ret_hdr;
+	int i, type_sec_size, err = 0;
+	uint32_t *ret_types;
+	void *raw_btf = NULL;
+
+	type_sec_size = get_raw_sec_size(raw_types);
+	if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
+		return NULL;
+
+	size_needed = sizeof(*hdr) + type_sec_size + str_sec_size;
+	raw_btf = malloc(size_needed);
+	if (CHECK(!raw_btf, "Cannot allocate memory for raw_btf"))
+		return NULL;
+
+	/* Copy header */
+	memcpy(raw_btf, hdr, sizeof(*hdr));
+	offset = sizeof(*hdr);
+
+	/* Index strings */
+	while ((next_str = get_next_str(next_str, end_str))) {
+		if (strs_cnt == strs_cap) {
+			strs_cap += max(16, strs_cap / 2);
+			tmp_strs_idx = realloc(strs_idx,
+					       sizeof(*strs_idx) * strs_cap);
+			if (CHECK(!tmp_strs_idx,
+				  "Cannot allocate memory for strs_idx")) {
+				err = -1;
+				goto done;
+			}
+			strs_idx = tmp_strs_idx;
+		}
+		strs_idx[strs_cnt++] = next_str;
+		next_str += strlen(next_str);
+	}
+
+	/* Copy type section */
+	ret_types = raw_btf + offset;
+	for (i = 0; i < type_sec_size / sizeof(raw_types[0]); i++) {
+		if (raw_types[i] == NAME_TBD) {
+			if (CHECK(next_str_idx == strs_cnt,
+				  "Error in getting next_str #%d",
+				  next_str_idx)) {
+				err = -1;
+				goto done;
+			}
+			ret_types[i] = strs_idx[next_str_idx++] - str;
+		} else if (IS_NAME_NTH(raw_types[i])) {
+			int idx = GET_NAME_NTH_IDX(raw_types[i]);
+
+			if (CHECK(idx <= 0 || idx > strs_cnt,
+				  "Error getting string #%d, strs_cnt:%d",
+				  idx, strs_cnt)) {
+				err = -1;
+				goto done;
+			}
+			ret_types[i] = strs_idx[idx-1] - str;
+		} else {
+			ret_types[i] = raw_types[i];
+		}
+	}
+	offset += type_sec_size;
+
+	/* Copy string section */
+	memcpy(raw_btf + offset, str, str_sec_size);
+
+	ret_hdr = (struct btf_header *)raw_btf;
+	ret_hdr->type_len = type_sec_size;
+	ret_hdr->str_off = type_sec_size;
+	ret_hdr->str_len = str_sec_size;
+
+	*btf_size = size_needed;
+	if (ret_next_str)
+		*ret_next_str =
+			next_str_idx < strs_cnt ? strs_idx[next_str_idx] : NULL;
+
+done:
+	if (err) {
+		if (raw_btf)
+			free(raw_btf);
+		if (strs_idx)
+			free(strs_idx);
+		return NULL;
+	}
+	return raw_btf;
+}
+
+const char *pprint_enum_str[] = {
+	"ENUM_ZERO",
+	"ENUM_ONE",
+	"ENUM_TWO",
+	"ENUM_THREE",
+};
+
+struct pprint_mapv {
+	uint32_t ui32;
+	uint16_t ui16;
+	/* 2 bytes hole */
+	int32_t si32;
+	uint32_t unused_bits2a:2,
+		bits28:28,
+		unused_bits2b:2;
+	union {
+		uint64_t ui64;
+		uint8_t ui8a[8];
+	};
+	enum {
+		ENUM_ZERO,
+		ENUM_ONE,
+		ENUM_TWO,
+		ENUM_THREE,
+	} aenum;
+	uint32_t ui32b;
+	uint32_t bits2c:2;
+	uint8_t si8_4[2][2];
+};
+
+#ifdef __SIZEOF_INT128__
+struct pprint_mapv_int128 {
+	__int128 si128a;
+	__int128 si128b;
+	unsigned __int128 bits3:3;
+	unsigned __int128 bits80:80;
+	unsigned __int128 ui128;
+};
+#endif
+
+static struct btf_raw_test pprint_test_template[] = {
+{
+	.raw_types = {
+		/* unsighed char */			/* [1] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+		/* unsigned short */			/* [2] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+		/* unsigned int */			/* [3] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+		/* int */				/* [4] */
+		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+		/* unsigned long long */		/* [5] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+		/* 2 bits */				/* [6] */
+		BTF_TYPE_INT_ENC(0, 0, 0, 2, 2),
+		/* 28 bits */				/* [7] */
+		BTF_TYPE_INT_ENC(0, 0, 0, 28, 4),
+		/* uint8_t[8] */			/* [8] */
+		BTF_TYPE_ARRAY_ENC(9, 1, 8),
+		/* typedef unsigned char uint8_t */	/* [9] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 1),
+		/* typedef unsigned short uint16_t */	/* [10] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 2),
+		/* typedef unsigned int uint32_t */	/* [11] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 3),
+		/* typedef int int32_t */		/* [12] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 4),
+		/* typedef unsigned long long uint64_t *//* [13] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 5),
+		/* union (anon) */			/* [14] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
+		/* enum (anon) */			/* [15] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+		BTF_ENUM_ENC(NAME_TBD, 0),
+		BTF_ENUM_ENC(NAME_TBD, 1),
+		BTF_ENUM_ENC(NAME_TBD, 2),
+		BTF_ENUM_ENC(NAME_TBD, 3),
+		/* struct pprint_mapv */		/* [16] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 11), 40),
+		BTF_MEMBER_ENC(NAME_TBD, 11, 0),	/* uint32_t ui32 */
+		BTF_MEMBER_ENC(NAME_TBD, 10, 32),	/* uint16_t ui16 */
+		BTF_MEMBER_ENC(NAME_TBD, 12, 64),	/* int32_t si32 */
+		BTF_MEMBER_ENC(NAME_TBD, 6, 96),	/* unused_bits2a */
+		BTF_MEMBER_ENC(NAME_TBD, 7, 98),	/* bits28 */
+		BTF_MEMBER_ENC(NAME_TBD, 6, 126),	/* unused_bits2b */
+		BTF_MEMBER_ENC(0, 14, 128),		/* union (anon) */
+		BTF_MEMBER_ENC(NAME_TBD, 15, 192),	/* aenum */
+		BTF_MEMBER_ENC(NAME_TBD, 11, 224),	/* uint32_t ui32b */
+		BTF_MEMBER_ENC(NAME_TBD, 6, 256),	/* bits2c */
+		BTF_MEMBER_ENC(NAME_TBD, 17, 264),	/* si8_4 */
+		BTF_TYPE_ARRAY_ENC(18, 1, 2),		/* [17] */
+		BTF_TYPE_ARRAY_ENC(1, 1, 2),		/* [18] */
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0si8_4"),
+	.key_size = sizeof(unsigned int),
+	.value_size = sizeof(struct pprint_mapv),
+	.key_type_id = 3,	/* unsigned int */
+	.value_type_id = 16,	/* struct pprint_mapv */
+	.max_entries = 128 * 1024,
+},
+
+{
+	/* this type will have the same type as the
+	 * first .raw_types definition, but struct type will
+	 * be encoded with kind_flag set.
+	 */
+	.raw_types = {
+		/* unsighed char */			/* [1] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+		/* unsigned short */			/* [2] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+		/* unsigned int */			/* [3] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+		/* int */				/* [4] */
+		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+		/* unsigned long long */		/* [5] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [6] */
+		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [7] */
+		/* uint8_t[8] */			/* [8] */
+		BTF_TYPE_ARRAY_ENC(9, 1, 8),
+		/* typedef unsigned char uint8_t */	/* [9] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 1),
+		/* typedef unsigned short uint16_t */	/* [10] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 2),
+		/* typedef unsigned int uint32_t */	/* [11] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 3),
+		/* typedef int int32_t */		/* [12] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 4),
+		/* typedef unsigned long long uint64_t *//* [13] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 5),
+		/* union (anon) */			/* [14] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
+		/* enum (anon) */			/* [15] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+		BTF_ENUM_ENC(NAME_TBD, 0),
+		BTF_ENUM_ENC(NAME_TBD, 1),
+		BTF_ENUM_ENC(NAME_TBD, 2),
+		BTF_ENUM_ENC(NAME_TBD, 3),
+		/* struct pprint_mapv */		/* [16] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 11), 40),
+		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),	/* uint32_t ui32 */
+		BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)),	/* uint16_t ui16 */
+		BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)),	/* int32_t si32 */
+		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 96)),	/* unused_bits2a */
+		BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)),	/* bits28 */
+		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 126)),	/* unused_bits2b */
+		BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),	/* union (anon) */
+		BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),	/* aenum */
+		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 224)),	/* uint32_t ui32b */
+		BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 256)),	/* bits2c */
+		BTF_MEMBER_ENC(NAME_TBD, 17, 264),	/* si8_4 */
+		BTF_TYPE_ARRAY_ENC(18, 1, 2),		/* [17] */
+		BTF_TYPE_ARRAY_ENC(1, 1, 2),		/* [18] */
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0si8_4"),
+	.key_size = sizeof(unsigned int),
+	.value_size = sizeof(struct pprint_mapv),
+	.key_type_id = 3,	/* unsigned int */
+	.value_type_id = 16,	/* struct pprint_mapv */
+	.max_entries = 128 * 1024,
+},
+
+{
+	/* this type will have the same layout as the
+	 * first .raw_types definition. The struct type will
+	 * be encoded with kind_flag set, bitfield members
+	 * are added typedef/const/volatile, and bitfield members
+	 * will have both int and enum types.
+	 */
+	.raw_types = {
+		/* unsighed char */			/* [1] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1),
+		/* unsigned short */			/* [2] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2),
+		/* unsigned int */			/* [3] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+		/* int */				/* [4] */
+		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
+		/* unsigned long long */		/* [5] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8),
+		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [6] */
+		BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),	/* [7] */
+		/* uint8_t[8] */			/* [8] */
+		BTF_TYPE_ARRAY_ENC(9, 1, 8),
+		/* typedef unsigned char uint8_t */	/* [9] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 1),
+		/* typedef unsigned short uint16_t */	/* [10] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 2),
+		/* typedef unsigned int uint32_t */	/* [11] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 3),
+		/* typedef int int32_t */		/* [12] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 4),
+		/* typedef unsigned long long uint64_t *//* [13] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 5),
+		/* union (anon) */			/* [14] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8),
+		BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */
+		BTF_MEMBER_ENC(NAME_TBD, 8, 0),	/* uint8_t ui8a[8]; */
+		/* enum (anon) */			/* [15] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4),
+		BTF_ENUM_ENC(NAME_TBD, 0),
+		BTF_ENUM_ENC(NAME_TBD, 1),
+		BTF_ENUM_ENC(NAME_TBD, 2),
+		BTF_ENUM_ENC(NAME_TBD, 3),
+		/* struct pprint_mapv */		/* [16] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 11), 40),
+		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)),	/* uint32_t ui32 */
+		BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)),	/* uint16_t ui16 */
+		BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)),	/* int32_t si32 */
+		BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 96)),	/* unused_bits2a */
+		BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)),	/* bits28 */
+		BTF_MEMBER_ENC(NAME_TBD, 19, BTF_MEMBER_OFFSET(2, 126)),/* unused_bits2b */
+		BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)),	/* union (anon) */
+		BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)),	/* aenum */
+		BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 224)),	/* uint32_t ui32b */
+		BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 256)),	/* bits2c */
+		BTF_MEMBER_ENC(NAME_TBD, 20, BTF_MEMBER_OFFSET(0, 264)),	/* si8_4 */
+		/* typedef unsigned int ___int */	/* [17] */
+		BTF_TYPEDEF_ENC(NAME_TBD, 18),
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), 6),	/* [18] */
+		BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 15),	/* [19] */
+		BTF_TYPE_ARRAY_ENC(21, 1, 2),					/* [20] */
+		BTF_TYPE_ARRAY_ENC(1, 1, 2),					/* [21] */
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0ui32b\0bits2c\0___int\0si8_4"),
+	.key_size = sizeof(unsigned int),
+	.value_size = sizeof(struct pprint_mapv),
+	.key_type_id = 3,	/* unsigned int */
+	.value_type_id = 16,	/* struct pprint_mapv */
+	.max_entries = 128 * 1024,
+},
+
+#ifdef __SIZEOF_INT128__
+{
+	/* test int128 */
+	.raw_types = {
+		/* unsigned int */				/* [1] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),
+		/* __int128 */					/* [2] */
+		BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 128, 16),
+		/* unsigned __int128 */				/* [3] */
+		BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 128, 16),
+		/* struct pprint_mapv_int128 */			/* [4] */
+		BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 5), 64),
+		BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)),		/* si128a */
+		BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 128)),		/* si128b */
+		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(3, 256)),		/* bits3 */
+		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(80, 259)),	/* bits80 */
+		BTF_MEMBER_ENC(NAME_TBD, 3, BTF_MEMBER_OFFSET(0, 384)),		/* ui128 */
+		BTF_END_RAW,
+	},
+	BTF_STR_SEC("\0unsigned int\0__int128\0unsigned __int128\0pprint_mapv_int128\0si128a\0si128b\0bits3\0bits80\0ui128"),
+	.key_size = sizeof(unsigned int),
+	.value_size = sizeof(struct pprint_mapv_int128),
+	.key_type_id = 1,
+	.value_type_id = 4,
+	.max_entries = 128 * 1024,
+	.mapv_kind = PPRINT_MAPV_KIND_INT128,
+},
+#endif
+
+};
+
+static struct btf_pprint_test_meta {
+	const char *descr;
+	enum bpf_map_type map_type;
+	const char *map_name;
+	bool ordered_map;
+	bool lossless_map;
+	bool percpu_map;
+} pprint_tests_meta[] = {
+{
+	.descr = "BTF pretty print array",
+	.map_type = BPF_MAP_TYPE_ARRAY,
+	.map_name = "pprint_test_array",
+	.ordered_map = true,
+	.lossless_map = true,
+	.percpu_map = false,
+},
+
+{
+	.descr = "BTF pretty print hash",
+	.map_type = BPF_MAP_TYPE_HASH,
+	.map_name = "pprint_test_hash",
+	.ordered_map = false,
+	.lossless_map = true,
+	.percpu_map = false,
+},
+
+{
+	.descr = "BTF pretty print lru hash",
+	.map_type = BPF_MAP_TYPE_LRU_HASH,
+	.map_name = "pprint_test_lru_hash",
+	.ordered_map = false,
+	.lossless_map = false,
+	.percpu_map = false,
+},
+
+{
+	.descr = "BTF pretty print percpu array",
+	.map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
+	.map_name = "pprint_test_percpu_array",
+	.ordered_map = true,
+	.lossless_map = true,
+	.percpu_map = true,
+},
+
+{
+	.descr = "BTF pretty print percpu hash",
+	.map_type = BPF_MAP_TYPE_PERCPU_HASH,
+	.map_name = "pprint_test_percpu_hash",
+	.ordered_map = false,
+	.lossless_map = true,
+	.percpu_map = true,
+},
+
+{
+	.descr = "BTF pretty print lru percpu hash",
+	.map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
+	.map_name = "pprint_test_lru_percpu_hash",
+	.ordered_map = false,
+	.lossless_map = false,
+	.percpu_map = true,
+},
+
+};
+
+static size_t get_pprint_mapv_size(enum pprint_mapv_kind_t mapv_kind)
+{
+	if (mapv_kind == PPRINT_MAPV_KIND_BASIC)
+		return sizeof(struct pprint_mapv);
+
+#ifdef __SIZEOF_INT128__
+	if (mapv_kind == PPRINT_MAPV_KIND_INT128)
+		return sizeof(struct pprint_mapv_int128);
+#endif
+
+	assert(0);
+}
+
+static void set_pprint_mapv(enum pprint_mapv_kind_t mapv_kind,
+			    void *mapv, uint32_t i,
+			    int num_cpus, int rounded_value_size)
+{
+	int cpu;
+
+	if (mapv_kind == PPRINT_MAPV_KIND_BASIC) {
+		struct pprint_mapv *v = mapv;
+
+		for (cpu = 0; cpu < num_cpus; cpu++) {
+			v->ui32 = i + cpu;
+			v->si32 = -i;
+			v->unused_bits2a = 3;
+			v->bits28 = i;
+			v->unused_bits2b = 3;
+			v->ui64 = i;
+			v->aenum = i & 0x03;
+			v->ui32b = 4;
+			v->bits2c = 1;
+			v->si8_4[0][0] = (cpu + i) & 0xff;
+			v->si8_4[0][1] = (cpu + i + 1) & 0xff;
+			v->si8_4[1][0] = (cpu + i + 2) & 0xff;
+			v->si8_4[1][1] = (cpu + i + 3) & 0xff;
+			v = (void *)v + rounded_value_size;
+		}
+	}
+
+#ifdef __SIZEOF_INT128__
+	if (mapv_kind == PPRINT_MAPV_KIND_INT128) {
+		struct pprint_mapv_int128 *v = mapv;
+
+		for (cpu = 0; cpu < num_cpus; cpu++) {
+			v->si128a = i;
+			v->si128b = -i;
+			v->bits3 = i & 0x07;
+			v->bits80 = (((unsigned __int128)1) << 64) + i;
+			v->ui128 = (((unsigned __int128)2) << 64) + i;
+			v = (void *)v + rounded_value_size;
+		}
+	}
+#endif
+}
+
+ssize_t get_pprint_expected_line(enum pprint_mapv_kind_t mapv_kind,
+				 char *expected_line, ssize_t line_size,
+				 bool percpu_map, unsigned int next_key,
+				 int cpu, void *mapv)
+{
+	ssize_t nexpected_line = -1;
+
+	if (mapv_kind == PPRINT_MAPV_KIND_BASIC) {
+		struct pprint_mapv *v = mapv;
+
+		nexpected_line = snprintf(expected_line, line_size,
+					  "%s%u: {%u,0,%d,0x%x,0x%x,0x%x,"
+					  "{%llu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s,"
+					  "%u,0x%x,[[%d,%d],[%d,%d]]}\n",
+					  percpu_map ? "\tcpu" : "",
+					  percpu_map ? cpu : next_key,
+					  v->ui32, v->si32,
+					  v->unused_bits2a,
+					  v->bits28,
+					  v->unused_bits2b,
+					  (__u64)v->ui64,
+					  v->ui8a[0], v->ui8a[1],
+					  v->ui8a[2], v->ui8a[3],
+					  v->ui8a[4], v->ui8a[5],
+					  v->ui8a[6], v->ui8a[7],
+					  pprint_enum_str[v->aenum],
+					  v->ui32b,
+					  v->bits2c,
+					  v->si8_4[0][0], v->si8_4[0][1],
+					  v->si8_4[1][0], v->si8_4[1][1]);
+	}
+
+#ifdef __SIZEOF_INT128__
+	if (mapv_kind == PPRINT_MAPV_KIND_INT128) {
+		struct pprint_mapv_int128 *v = mapv;
+
+		nexpected_line = snprintf(expected_line, line_size,
+					  "%s%u: {0x%lx,0x%lx,0x%lx,"
+					  "0x%lx%016lx,0x%lx%016lx}\n",
+					  percpu_map ? "\tcpu" : "",
+					  percpu_map ? cpu : next_key,
+					  (uint64_t)v->si128a,
+					  (uint64_t)v->si128b,
+					  (uint64_t)v->bits3,
+					  (uint64_t)(v->bits80 >> 64),
+					  (uint64_t)v->bits80,
+					  (uint64_t)(v->ui128 >> 64),
+					  (uint64_t)v->ui128);
+	}
+#endif
+
+	return nexpected_line;
+}
+
+static int check_line(const char *expected_line, int nexpected_line,
+		      int expected_line_len, const char *line)
+{
+	if (CHECK(nexpected_line == expected_line_len,
+		  "expected_line is too long"))
+		return -1;
+
+	if (strcmp(expected_line, line)) {
+		fprintf(stderr, "unexpected pprint output\n");
+		fprintf(stderr, "expected: %s", expected_line);
+		fprintf(stderr, "    read: %s", line);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int do_test_pprint(int test_num)
+{
+	const struct btf_raw_test *test = &pprint_test_template[test_num];
+	enum pprint_mapv_kind_t mapv_kind = test->mapv_kind;
+	struct bpf_create_map_attr create_attr = {};
+	bool ordered_map, lossless_map, percpu_map;
+	int err, ret, num_cpus, rounded_value_size;
+	unsigned int key, nr_read_elems;
+	int map_fd = -1, btf_fd = -1;
+	unsigned int raw_btf_size;
+	char expected_line[255];
+	FILE *pin_file = NULL;
+	char pin_path[255];
+	size_t line_len = 0;
+	char *line = NULL;
+	void *mapv = NULL;
+	uint8_t *raw_btf;
+	ssize_t nread;
+
+	fprintf(stderr, "%s(#%d)......", test->descr, test_num);
+	raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
+				 test->str_sec, test->str_sec_size,
+				 &raw_btf_size, NULL);
+
+	if (!raw_btf)
+		return -1;
+
+	*btf_log_buf = '\0';
+	btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+			      btf_log_buf, BTF_LOG_BUF_SIZE,
+			      args.always_log);
+	free(raw_btf);
+
+	if (CHECK(btf_fd == -1, "errno:%d", errno)) {
+		err = -1;
+		goto done;
+	}
+
+	create_attr.name = test->map_name;
+	create_attr.map_type = test->map_type;
+	create_attr.key_size = test->key_size;
+	create_attr.value_size = test->value_size;
+	create_attr.max_entries = test->max_entries;
+	create_attr.btf_fd = btf_fd;
+	create_attr.btf_key_type_id = test->key_type_id;
+	create_attr.btf_value_type_id = test->value_type_id;
+
+	map_fd = bpf_create_map_xattr(&create_attr);
+	if (CHECK(map_fd == -1, "errno:%d", errno)) {
+		err = -1;
+		goto done;
+	}
+
+	ret = snprintf(pin_path, sizeof(pin_path), "%s/%s",
+		       "/sys/fs/bpf", test->map_name);
+
+	if (CHECK(ret == sizeof(pin_path), "pin_path %s/%s is too long",
+		  "/sys/fs/bpf", test->map_name)) {
+		err = -1;
+		goto done;
+	}
+
+	err = bpf_obj_pin(map_fd, pin_path);
+	if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
+		goto done;
+
+	percpu_map = test->percpu_map;
+	num_cpus = percpu_map ? bpf_num_possible_cpus() : 1;
+	rounded_value_size = round_up(get_pprint_mapv_size(mapv_kind), 8);
+	mapv = calloc(num_cpus, rounded_value_size);
+	if (CHECK(!mapv, "mapv allocation failure")) {
+		err = -1;
+		goto done;
+	}
+
+	for (key = 0; key < test->max_entries; key++) {
+		set_pprint_mapv(mapv_kind, mapv, key, num_cpus, rounded_value_size);
+		bpf_map_update_elem(map_fd, &key, mapv, 0);
+	}
+
+	pin_file = fopen(pin_path, "r");
+	if (CHECK(!pin_file, "fopen(%s): errno:%d", pin_path, errno)) {
+		err = -1;
+		goto done;
+	}
+
+	/* Skip lines start with '#' */
+	while ((nread = getline(&line, &line_len, pin_file)) > 0 &&
+	       *line == '#')
+		;
+
+	if (CHECK(nread <= 0, "Unexpected EOF")) {
+		err = -1;
+		goto done;
+	}
+
+	nr_read_elems = 0;
+	ordered_map = test->ordered_map;
+	lossless_map = test->lossless_map;
+	do {
+		ssize_t nexpected_line;
+		unsigned int next_key;
+		void *cmapv;
+		int cpu;
+
+		next_key = ordered_map ? nr_read_elems : atoi(line);
+		set_pprint_mapv(mapv_kind, mapv, next_key, num_cpus, rounded_value_size);
+		cmapv = mapv;
+
+		for (cpu = 0; cpu < num_cpus; cpu++) {
+			if (percpu_map) {
+				/* for percpu map, the format looks like:
+				 * <key>: {
+				 *	cpu0: <value_on_cpu0>
+				 *	cpu1: <value_on_cpu1>
+				 *	...
+				 *	cpun: <value_on_cpun>
+				 * }
+				 *
+				 * let us verify the line containing the key here.
+				 */
+				if (cpu == 0) {
+					nexpected_line = snprintf(expected_line,
+								  sizeof(expected_line),
+								  "%u: {\n",
+								  next_key);
+
+					err = check_line(expected_line, nexpected_line,
+							 sizeof(expected_line), line);
+					if (err == -1)
+						goto done;
+				}
+
+				/* read value@cpu */
+				nread = getline(&line, &line_len, pin_file);
+				if (nread < 0)
+					break;
+			}
+
+			nexpected_line = get_pprint_expected_line(mapv_kind, expected_line,
+								  sizeof(expected_line),
+								  percpu_map, next_key,
+								  cpu, cmapv);
+			err = check_line(expected_line, nexpected_line,
+					 sizeof(expected_line), line);
+			if (err == -1)
+				goto done;
+
+			cmapv = cmapv + rounded_value_size;
+		}
+
+		if (percpu_map) {
+			/* skip the last bracket for the percpu map */
+			nread = getline(&line, &line_len, pin_file);
+			if (nread < 0)
+				break;
+		}
+
+		nread = getline(&line, &line_len, pin_file);
+	} while (++nr_read_elems < test->max_entries && nread > 0);
+
+	if (lossless_map &&
+	    CHECK(nr_read_elems < test->max_entries,
+		  "Unexpected EOF. nr_read_elems:%u test->max_entries:%u",
+		  nr_read_elems, test->max_entries)) {
+		err = -1;
+		goto done;
+	}
+
+	if (CHECK(nread > 0, "Unexpected extra pprint output: %s", line)) {
+		err = -1;
+		goto done;
+	}
+
+	err = 0;
+
+done:
+	if (mapv)
+		free(mapv);
+	if (!err)
+		fprintf(stderr, "OK");
+	if (*btf_log_buf && (err || args.always_log))
+		fprintf(stderr, "\n%s", btf_log_buf);
+	if (btf_fd != -1)
+		close(btf_fd);
+	if (map_fd != -1)
+		close(map_fd);
+	if (pin_file)
+		fclose(pin_file);
+	unlink(pin_path);
+	free(line);
+
+	return err;
+}
+
+static int test_pprint(void)
+{
+	unsigned int i;
+	int err = 0;
+
+	/* test various maps with the first test template */
+	for (i = 0; i < ARRAY_SIZE(pprint_tests_meta); i++) {
+		pprint_test_template[0].descr = pprint_tests_meta[i].descr;
+		pprint_test_template[0].map_type = pprint_tests_meta[i].map_type;
+		pprint_test_template[0].map_name = pprint_tests_meta[i].map_name;
+		pprint_test_template[0].ordered_map = pprint_tests_meta[i].ordered_map;
+		pprint_test_template[0].lossless_map = pprint_tests_meta[i].lossless_map;
+		pprint_test_template[0].percpu_map = pprint_tests_meta[i].percpu_map;
+
+		err |= count_result(do_test_pprint(0));
+	}
+
+	/* test rest test templates with the first map */
+	for (i = 1; i < ARRAY_SIZE(pprint_test_template); i++) {
+		pprint_test_template[i].descr = pprint_tests_meta[0].descr;
+		pprint_test_template[i].map_type = pprint_tests_meta[0].map_type;
+		pprint_test_template[i].map_name = pprint_tests_meta[0].map_name;
+		pprint_test_template[i].ordered_map = pprint_tests_meta[0].ordered_map;
+		pprint_test_template[i].lossless_map = pprint_tests_meta[0].lossless_map;
+		pprint_test_template[i].percpu_map = pprint_tests_meta[0].percpu_map;
+		err |= count_result(do_test_pprint(i));
+	}
+
+	return err;
+}
+
+static void usage(const char *cmd)
+{
+	fprintf(stderr, "Usage: %s [-l]\n", cmd);
+}
+
+static int parse_args(int argc, char **argv)
+{
+	const char *optstr = "hlpk:f:r:g:d:";
+	int opt;
+
+	while ((opt = getopt(argc, argv, optstr)) != -1) {
+		switch (opt) {
+		case 'l':
+			args.always_log = true;
+			break;
+		case 'h':
+			usage(argv[0]);
+			exit(0);
+		default:
+			usage(argv[0]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static void print_summary(void)
+{
+	fprintf(stderr, "PASS:%u SKIP:%u FAIL:%u\n",
+		pass_cnt - skip_cnt, skip_cnt, error_cnt);
+}
+
+int main(int argc, char **argv)
+{
+	int err = 0;
+
+	err = parse_args(argc, argv);
+	if (err)
+		return err;
+
+	if (args.always_log)
+		libbpf_set_print(__base_pr);
+
+	test_pprint();
+
+	print_summary();
+	return err;
+}