diff mbox series

[bpf-next] selftests/bpf: add more stats into veristat

Message ID 20241205193404.629861-1-mykyta.yatsenko5@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series [bpf-next] selftests/bpf: add more stats into veristat | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for bpf-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/build_tools success Errors and warnings before: 0 (+0) this patch: 0 (+0)
netdev/cc_maintainers warning 12 maintainers not CCed: sdf@fomichev.me haoluo@google.com eddyz87@gmail.com kpsingh@kernel.org john.fastabend@gmail.com shuah@kernel.org martin.lau@linux.dev linux-kselftest@vger.kernel.org jolsa@kernel.org yonghong.song@linux.dev song@kernel.org mykolal@fb.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: Comparisons should place the constant on the right side of the test WARNING: line length of 142 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-43 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-45 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-44 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-13 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-14 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-7 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2

Commit Message

Mykyta Yatsenko Dec. 5, 2024, 7:34 p.m. UTC
From: Mykyta Yatsenko <yatsenko@meta.com>

Extend veristat to collect and print more stats, namely:
- program size in instructions
- jited program size
- program type
- attach type
- stack depth

Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
---
 tools/testing/selftests/bpf/veristat.c | 51 +++++++++++++++++++++++---
 1 file changed, 46 insertions(+), 5 deletions(-)

Comments

Andrii Nakryiko Dec. 5, 2024, 11:50 p.m. UTC | #1
On Thu, Dec 5, 2024 at 11:34 AM Mykyta Yatsenko
<mykyta.yatsenko5@gmail.com> wrote:
>
> From: Mykyta Yatsenko <yatsenko@meta.com>
>
> Extend veristat to collect and print more stats, namely:
> - program size in instructions
> - jited program size
> - program type
> - attach type
> - stack depth
>
> Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
> ---
>  tools/testing/selftests/bpf/veristat.c | 51 +++++++++++++++++++++++---
>  1 file changed, 46 insertions(+), 5 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
> index e12ef953fba8..0d7fb00175e8 100644
> --- a/tools/testing/selftests/bpf/veristat.c
> +++ b/tools/testing/selftests/bpf/veristat.c
> @@ -38,8 +38,14 @@ enum stat_id {
>         FILE_NAME,
>         PROG_NAME,
>
> +       SIZE,
> +       JITED_SIZE,
> +       STACK,
> +       PROG_TYPE,
> +       ATTACH_TYPE,
> +
>         ALL_STATS_CNT,
> -       NUM_STATS_CNT = FILE_NAME - VERDICT,
> +       NUM_STATS_CNT = ATTACH_TYPE - VERDICT + 1,

this doesn't sound right, because PROG_NAME isn't a number statistics

>  };
>
>  /* In comparison mode each stat can specify up to four different values:
> @@ -640,19 +646,22 @@ static int append_filter_file(const char *path)
>  }
>
>  static const struct stat_specs default_output_spec = {
> -       .spec_cnt = 7,
> +       .spec_cnt = 12,
>         .ids = {
>                 FILE_NAME, PROG_NAME, VERDICT, DURATION,
> -               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
> +               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, SIZE,
> +               JITED_SIZE, PROG_TYPE, ATTACH_TYPE, STACK,

I think SIZE or JITED_SIZE might be good candidates for default view,
but not all of the above. I think we can also drop PEAK_STATES from
default, btw.

>         },
>  };
>
>  static const struct stat_specs default_csv_output_spec = {
> -       .spec_cnt = 9,
> +       .spec_cnt = 14,
>         .ids = {
>                 FILE_NAME, PROG_NAME, VERDICT, DURATION,
>                 TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
>                 MAX_STATES_PER_INSN, MARK_READ_MAX_LEN,
> +               SIZE, JITED_SIZE, PROG_TYPE, ATTACH_TYPE,
> +               STACK,

this is fine, we want everything in CSV, yep

>         },
>  };
>
> @@ -688,6 +697,11 @@ static struct stat_def {
>         [PEAK_STATES] = { "Peak states", {"peak_states"}, },
>         [MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
>         [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
> +       [SIZE] = { "Prog size", {"prog_size", "size"}, },

drop "size" alias, it's too ambiguous?

> +       [JITED_SIZE] = { "Jited size", {"jited_size"}, },

this probably should be prog_size_jited or something like that (I
know, verbose, but unambiguous)

> +       [STACK] = {"Stack depth", {"stack_depth", "stack"}, },
> +       [PROG_TYPE] = { "Program type", {"program_type", "prog_type"}, },

let's drop "program_type", verbose

> +       [ATTACH_TYPE] = { "Attach type", {"attach_type", }, },
>  };
>
>  static bool parse_stat_id_var(const char *name, size_t len, int *id,
> @@ -853,13 +867,16 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
>
>                 if (1 == sscanf(cur, "verification time %ld usec\n", &s->stats[DURATION]))
>                         continue;
> -               if (6 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
> +               if (5 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",

is this a preexisting bug? why we didn't catch it before?

>                                 &s->stats[TOTAL_INSNS],
>                                 &s->stats[MAX_STATES_PER_INSN],
>                                 &s->stats[TOTAL_STATES],
>                                 &s->stats[PEAK_STATES],
>                                 &s->stats[MARK_READ_MAX_LEN]))
>                         continue;
> +
> +               if (1 == sscanf(cur, "stack depth %ld", &s->stats[STACK]))

heh, not so simple, actually. stack depth is actually a list of stack
sizes for main program and each subprogram. Try

sudo ./veristat test_subprogs.bpf.o -v

stack depth 8+8+0+0+8+0

so we have to make some choices here, actually... we either parse that
and add up, and/or we parse all that and associate it with individual
subprograms.

I think we can start with the former, but the latter is actually
useful and quite tricky for humans to figure out because that order
depends on libbpf-controlled order of subprograms (which veristat can
get from btf_ext, I believe). Not sure if we need/want to record
by-subprog breakdown into CSV, but it would be useful to have a more
detailed breakdown by subprog in some verbose mode. Let's think about
that.

> +                       continue;
>         }
>
>         return 0;
> @@ -1146,8 +1163,11 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>         char *buf;
>         int buf_sz, log_level;
>         struct verif_stats *stats;
> +       struct bpf_prog_info info = {};

this should be initialized with memset(0)

> +       __u32 info_len = sizeof(info);
>         int err = 0;
>         void *tmp;
> +       int fd;
>
>         if (!should_process_file_prog(base_filename, bpf_program__name(prog))) {
>                 env.progs_skipped++;
> @@ -1196,6 +1216,13 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>         stats->file_name = strdup(base_filename);
>         stats->prog_name = strdup(bpf_program__name(prog));
>         stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
> +       stats->stats[SIZE] = bpf_program__insn_cnt(prog);
> +       stats->stats[PROG_TYPE] = bpf_program__type(prog);
> +       stats->stats[ATTACH_TYPE] = bpf_program__expected_attach_type(prog);
> +       fd = bpf_program__fd(prog);
> +       if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
> +               stats->stats[JITED_SIZE] = info.jited_prog_len;
> +

please check that this is total length including all the subprogs

>         parse_verif_log(buf, buf_sz, stats);
>
>         if (env.verbose) {
> @@ -1309,6 +1336,11 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
>         case PROG_NAME:
>                 cmp = strcmp(s1->prog_name, s2->prog_name);
>                 break;
> +       case ATTACH_TYPE:
> +       case PROG_TYPE:
> +       case SIZE:
> +       case JITED_SIZE:
> +       case STACK:
>         case VERDICT:
>         case DURATION:
>         case TOTAL_INSNS:
> @@ -1523,12 +1555,21 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id,
>                 else
>                         *str = s->stats[VERDICT] ? "success" : "failure";
>                 break;
> +       case ATTACH_TYPE:
> +               *str = s ? libbpf_bpf_attach_type_str(s->stats[ATTACH_TYPE]) ? : "N/A" : "N/A";
> +               break;
> +       case PROG_TYPE:
> +               *str = s ? libbpf_bpf_prog_type_str(s->stats[PROG_TYPE]) ? : "N/A" : "N/A";

let's not have x ? y ? z pattern, please do explicit outer if like we
do for VERDICT

pw-bot: cr

> +               break;
>         case DURATION:
>         case TOTAL_INSNS:
>         case TOTAL_STATES:
>         case PEAK_STATES:
>         case MAX_STATES_PER_INSN:
>         case MARK_READ_MAX_LEN:
> +       case STACK:
> +       case SIZE:
> +       case JITED_SIZE:
>                 *val = s ? s->stats[id] : 0;
>                 break;
>         default:
> --
> 2.47.1
>
Mykyta Yatsenko Dec. 6, 2024, 9:54 a.m. UTC | #2
On 05/12/2024 23:50, Andrii Nakryiko wrote:
> On Thu, Dec 5, 2024 at 11:34 AM Mykyta Yatsenko
> <mykyta.yatsenko5@gmail.com> wrote:
>> From: Mykyta Yatsenko <yatsenko@meta.com>
>>
>> Extend veristat to collect and print more stats, namely:
>> - program size in instructions
>> - jited program size
>> - program type
>> - attach type
>> - stack depth
>>
>> Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
>> ---
>>   tools/testing/selftests/bpf/veristat.c | 51 +++++++++++++++++++++++---
>>   1 file changed, 46 insertions(+), 5 deletions(-)
>>
>> diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
>> index e12ef953fba8..0d7fb00175e8 100644
>> --- a/tools/testing/selftests/bpf/veristat.c
>> +++ b/tools/testing/selftests/bpf/veristat.c
>> @@ -38,8 +38,14 @@ enum stat_id {
>>          FILE_NAME,
>>          PROG_NAME,
>>
>> +       SIZE,
>> +       JITED_SIZE,
>> +       STACK,
>> +       PROG_TYPE,
>> +       ATTACH_TYPE,
>> +
>>          ALL_STATS_CNT,
>> -       NUM_STATS_CNT = FILE_NAME - VERDICT,
>> +       NUM_STATS_CNT = ATTACH_TYPE - VERDICT + 1,
> this doesn't sound right, because PROG_NAME isn't a number statistics
I did not realize NUM_STATS_CNT means count of number statistics, now 
this makes sense, thanks.
>>   };
>>
>>   /* In comparison mode each stat can specify up to four different values:
>> @@ -640,19 +646,22 @@ static int append_filter_file(const char *path)
>>   }
>>
>>   static const struct stat_specs default_output_spec = {
>> -       .spec_cnt = 7,
>> +       .spec_cnt = 12,
>>          .ids = {
>>                  FILE_NAME, PROG_NAME, VERDICT, DURATION,
>> -               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
>> +               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, SIZE,
>> +               JITED_SIZE, PROG_TYPE, ATTACH_TYPE, STACK,
> I think SIZE or JITED_SIZE might be good candidates for default view,
> but not all of the above. I think we can also drop PEAK_STATES from
> default, btw.
>
>>          },
>>   };
>>
>>   static const struct stat_specs default_csv_output_spec = {
>> -       .spec_cnt = 9,
>> +       .spec_cnt = 14,
>>          .ids = {
>>                  FILE_NAME, PROG_NAME, VERDICT, DURATION,
>>                  TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
>>                  MAX_STATES_PER_INSN, MARK_READ_MAX_LEN,
>> +               SIZE, JITED_SIZE, PROG_TYPE, ATTACH_TYPE,
>> +               STACK,
> this is fine, we want everything in CSV, yep
>
>>          },
>>   };
>>
>> @@ -688,6 +697,11 @@ static struct stat_def {
>>          [PEAK_STATES] = { "Peak states", {"peak_states"}, },
>>          [MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
>>          [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
>> +       [SIZE] = { "Prog size", {"prog_size", "size"}, },
> drop "size" alias, it's too ambiguous?
>
>> +       [JITED_SIZE] = { "Jited size", {"jited_size"}, },
> this probably should be prog_size_jited or something like that (I
> know, verbose, but unambiguous)
>
>> +       [STACK] = {"Stack depth", {"stack_depth", "stack"}, },
>> +       [PROG_TYPE] = { "Program type", {"program_type", "prog_type"}, },
> let's drop "program_type", verbose
>
>> +       [ATTACH_TYPE] = { "Attach type", {"attach_type", }, },
>>   };
>>
>>   static bool parse_stat_id_var(const char *name, size_t len, int *id,
>> @@ -853,13 +867,16 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
>>
>>                  if (1 == sscanf(cur, "verification time %ld usec\n", &s->stats[DURATION]))
>>                          continue;
>> -               if (6 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
>> +               if (5 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
> is this a preexisting bug? why we didn't catch it before?
Nothing is broken because of this, sscanf sets all 5 variables either 
way. Currently we continue ongoing iteration of the loop, instead of jumping
to the next immediately. We can drop these checks at all, it's not going 
to change correctness of this code.
>>                                  &s->stats[TOTAL_INSNS],
>>                                  &s->stats[MAX_STATES_PER_INSN],
>>                                  &s->stats[TOTAL_STATES],
>>                                  &s->stats[PEAK_STATES],
>>                                  &s->stats[MARK_READ_MAX_LEN]))
>>                          continue;
>> +
>> +               if (1 == sscanf(cur, "stack depth %ld", &s->stats[STACK]))
> heh, not so simple, actually. stack depth is actually a list of stack
> sizes for main program and each subprogram. Try
>
> sudo ./veristat test_subprogs.bpf.o -v
>
> stack depth 8+8+0+0+8+0
>
> so we have to make some choices here, actually... we either parse that
> and add up, and/or we parse all that and associate it with individual
> subprograms.
>
> I think we can start with the former, but the latter is actually
> useful and quite tricky for humans to figure out because that order
> depends on libbpf-controlled order of subprograms (which veristat can
> get from btf_ext, I believe). Not sure if we need/want to record
> by-subprog breakdown into CSV, but it would be useful to have a more
> detailed breakdown by subprog in some verbose mode. Let's think about
> that.
>
>> +                       continue;
>>          }
>>
>>          return 0;
>> @@ -1146,8 +1163,11 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>          char *buf;
>>          int buf_sz, log_level;
>>          struct verif_stats *stats;
>> +       struct bpf_prog_info info = {};
> this should be initialized with memset(0)
>
>> +       __u32 info_len = sizeof(info);
>>          int err = 0;
>>          void *tmp;
>> +       int fd;
>>
>>          if (!should_process_file_prog(base_filename, bpf_program__name(prog))) {
>>                  env.progs_skipped++;
>> @@ -1196,6 +1216,13 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>          stats->file_name = strdup(base_filename);
>>          stats->prog_name = strdup(bpf_program__name(prog));
>>          stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
>> +       stats->stats[SIZE] = bpf_program__insn_cnt(prog);
>> +       stats->stats[PROG_TYPE] = bpf_program__type(prog);
>> +       stats->stats[ATTACH_TYPE] = bpf_program__expected_attach_type(prog);
>> +       fd = bpf_program__fd(prog);
>> +       if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
>> +               stats->stats[JITED_SIZE] = info.jited_prog_len;
>> +
> please check that this is total length including all the subprogs
>
>>          parse_verif_log(buf, buf_sz, stats);
>>
>>          if (env.verbose) {
>> @@ -1309,6 +1336,11 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
>>          case PROG_NAME:
>>                  cmp = strcmp(s1->prog_name, s2->prog_name);
>>                  break;
>> +       case ATTACH_TYPE:
>> +       case PROG_TYPE:
>> +       case SIZE:
>> +       case JITED_SIZE:
>> +       case STACK:
>>          case VERDICT:
>>          case DURATION:
>>          case TOTAL_INSNS:
>> @@ -1523,12 +1555,21 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id,
>>                  else
>>                          *str = s->stats[VERDICT] ? "success" : "failure";
>>                  break;
>> +       case ATTACH_TYPE:
>> +               *str = s ? libbpf_bpf_attach_type_str(s->stats[ATTACH_TYPE]) ? : "N/A" : "N/A";
>> +               break;
>> +       case PROG_TYPE:
>> +               *str = s ? libbpf_bpf_prog_type_str(s->stats[PROG_TYPE]) ? : "N/A" : "N/A";
> let's not have x ? y ? z pattern, please do explicit outer if like we
> do for VERDICT
>
> pw-bot: cr
>
>> +               break;
>>          case DURATION:
>>          case TOTAL_INSNS:
>>          case TOTAL_STATES:
>>          case PEAK_STATES:
>>          case MAX_STATES_PER_INSN:
>>          case MARK_READ_MAX_LEN:
>> +       case STACK:
>> +       case SIZE:
>> +       case JITED_SIZE:
>>                  *val = s ? s->stats[id] : 0;
>>                  break;
>>          default:
>> --
>> 2.47.1
>>
Mykyta Yatsenko Dec. 6, 2024, 1:29 p.m. UTC | #3
On 05/12/2024 23:50, Andrii Nakryiko wrote:
> On Thu, Dec 5, 2024 at 11:34 AM Mykyta Yatsenko
> <mykyta.yatsenko5@gmail.com> wrote:
>> From: Mykyta Yatsenko <yatsenko@meta.com>
>>
>> Extend veristat to collect and print more stats, namely:
>> - program size in instructions
>> - jited program size
>> - program type
>> - attach type
>> - stack depth
>>
>> Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
>> ---
>>   tools/testing/selftests/bpf/veristat.c | 51 +++++++++++++++++++++++---
>>   1 file changed, 46 insertions(+), 5 deletions(-)
>>
>> diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
>> index e12ef953fba8..0d7fb00175e8 100644
>> --- a/tools/testing/selftests/bpf/veristat.c
>> +++ b/tools/testing/selftests/bpf/veristat.c
>> @@ -38,8 +38,14 @@ enum stat_id {
>>          FILE_NAME,
>>          PROG_NAME,
>>
>> +       SIZE,
>> +       JITED_SIZE,
>> +       STACK,
>> +       PROG_TYPE,
>> +       ATTACH_TYPE,
>> +
>>          ALL_STATS_CNT,
>> -       NUM_STATS_CNT = FILE_NAME - VERDICT,
>> +       NUM_STATS_CNT = ATTACH_TYPE - VERDICT + 1,
> this doesn't sound right, because PROG_NAME isn't a number statistics
>
>>   };
>>
>>   /* In comparison mode each stat can specify up to four different values:
>> @@ -640,19 +646,22 @@ static int append_filter_file(const char *path)
>>   }
>>
>>   static const struct stat_specs default_output_spec = {
>> -       .spec_cnt = 7,
>> +       .spec_cnt = 12,
>>          .ids = {
>>                  FILE_NAME, PROG_NAME, VERDICT, DURATION,
>> -               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
>> +               TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, SIZE,
>> +               JITED_SIZE, PROG_TYPE, ATTACH_TYPE, STACK,
> I think SIZE or JITED_SIZE might be good candidates for default view,
> but not all of the above. I think we can also drop PEAK_STATES from
> default, btw.
>
>>          },
>>   };
>>
>>   static const struct stat_specs default_csv_output_spec = {
>> -       .spec_cnt = 9,
>> +       .spec_cnt = 14,
>>          .ids = {
>>                  FILE_NAME, PROG_NAME, VERDICT, DURATION,
>>                  TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
>>                  MAX_STATES_PER_INSN, MARK_READ_MAX_LEN,
>> +               SIZE, JITED_SIZE, PROG_TYPE, ATTACH_TYPE,
>> +               STACK,
> this is fine, we want everything in CSV, yep
>
>>          },
>>   };
>>
>> @@ -688,6 +697,11 @@ static struct stat_def {
>>          [PEAK_STATES] = { "Peak states", {"peak_states"}, },
>>          [MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
>>          [MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
>> +       [SIZE] = { "Prog size", {"prog_size", "size"}, },
> drop "size" alias, it's too ambiguous?
>
>> +       [JITED_SIZE] = { "Jited size", {"jited_size"}, },
> this probably should be prog_size_jited or something like that (I
> know, verbose, but unambiguous)
>
>> +       [STACK] = {"Stack depth", {"stack_depth", "stack"}, },
>> +       [PROG_TYPE] = { "Program type", {"program_type", "prog_type"}, },
> let's drop "program_type", verbose
>
>> +       [ATTACH_TYPE] = { "Attach type", {"attach_type", }, },
>>   };
>>
>>   static bool parse_stat_id_var(const char *name, size_t len, int *id,
>> @@ -853,13 +867,16 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
>>
>>                  if (1 == sscanf(cur, "verification time %ld usec\n", &s->stats[DURATION]))
>>                          continue;
>> -               if (6 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
>> +               if (5 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
> is this a preexisting bug? why we didn't catch it before?
>
>>                                  &s->stats[TOTAL_INSNS],
>>                                  &s->stats[MAX_STATES_PER_INSN],
>>                                  &s->stats[TOTAL_STATES],
>>                                  &s->stats[PEAK_STATES],
>>                                  &s->stats[MARK_READ_MAX_LEN]))
>>                          continue;
>> +
>> +               if (1 == sscanf(cur, "stack depth %ld", &s->stats[STACK]))
> heh, not so simple, actually. stack depth is actually a list of stack
> sizes for main program and each subprogram. Try
>
> sudo ./veristat test_subprogs.bpf.o -v
>
> stack depth 8+8+0+0+8+0
>
> so we have to make some choices here, actually... we either parse that
> and add up, and/or we parse all that and associate it with individual
> subprograms.
>
> I think we can start with the former, but the latter is actually
> useful and quite tricky for humans to figure out because that order
> depends on libbpf-controlled order of subprograms (which veristat can
> get from btf_ext, I believe). Not sure if we need/want to record
> by-subprog breakdown into CSV, but it would be useful to have a more
> detailed breakdown by subprog in some verbose mode. Let's think about
> that.
>
>> +                       continue;
>>          }
>>
>>          return 0;
>> @@ -1146,8 +1163,11 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>          char *buf;
>>          int buf_sz, log_level;
>>          struct verif_stats *stats;
>> +       struct bpf_prog_info info = {};
> this should be initialized with memset(0)
>
>> +       __u32 info_len = sizeof(info);
>>          int err = 0;
>>          void *tmp;
>> +       int fd;
>>
>>          if (!should_process_file_prog(base_filename, bpf_program__name(prog))) {
>>                  env.progs_skipped++;
>> @@ -1196,6 +1216,13 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>          stats->file_name = strdup(base_filename);
>>          stats->prog_name = strdup(bpf_program__name(prog));
>>          stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
>> +       stats->stats[SIZE] = bpf_program__insn_cnt(prog);
>> +       stats->stats[PROG_TYPE] = bpf_program__type(prog);
>> +       stats->stats[ATTACH_TYPE] = bpf_program__expected_attach_type(prog);
>> +       fd = bpf_program__fd(prog);
>> +       if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
>> +               stats->stats[JITED_SIZE] = info.jited_prog_len;
>> +
> please check that this is total length including all the subprogs
I think it does include subprogs. I checked this by loading program:
`bpftool prog load test_subprogs.bpf.o /sys/fs/bpf/test_subprogs`
then print its jited code:
`bpftool prog dump jited name prog1 test_subprogs.bpf.o`
summed sizes across all functions and compared with veristat output:
```
File                 Program  Verdict Duration (us)  Insns  States  Prog 
size  Jited size
-------------------  -------  -------  -------------  ----- ------  
---------  ----------
test_subprogs.bpf.o  prog1    success            181     56 6         
54         333
```
>>          parse_verif_log(buf, buf_sz, stats);
>>
>>          if (env.verbose) {
>> @@ -1309,6 +1336,11 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
>>          case PROG_NAME:
>>                  cmp = strcmp(s1->prog_name, s2->prog_name);
>>                  break;
>> +       case ATTACH_TYPE:
>> +       case PROG_TYPE:
>> +       case SIZE:
>> +       case JITED_SIZE:
>> +       case STACK:
>>          case VERDICT:
>>          case DURATION:
>>          case TOTAL_INSNS:
>> @@ -1523,12 +1555,21 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id,
>>                  else
>>                          *str = s->stats[VERDICT] ? "success" : "failure";
>>                  break;
>> +       case ATTACH_TYPE:
>> +               *str = s ? libbpf_bpf_attach_type_str(s->stats[ATTACH_TYPE]) ? : "N/A" : "N/A";
>> +               break;
>> +       case PROG_TYPE:
>> +               *str = s ? libbpf_bpf_prog_type_str(s->stats[PROG_TYPE]) ? : "N/A" : "N/A";
> let's not have x ? y ? z pattern, please do explicit outer if like we
> do for VERDICT
>
> pw-bot: cr
>
>> +               break;
>>          case DURATION:
>>          case TOTAL_INSNS:
>>          case TOTAL_STATES:
>>          case PEAK_STATES:
>>          case MAX_STATES_PER_INSN:
>>          case MARK_READ_MAX_LEN:
>> +       case STACK:
>> +       case SIZE:
>> +       case JITED_SIZE:
>>                  *val = s ? s->stats[id] : 0;
>>                  break;
>>          default:
>> --
>> 2.47.1
>>
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
index e12ef953fba8..0d7fb00175e8 100644
--- a/tools/testing/selftests/bpf/veristat.c
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -38,8 +38,14 @@  enum stat_id {
 	FILE_NAME,
 	PROG_NAME,
 
+	SIZE,
+	JITED_SIZE,
+	STACK,
+	PROG_TYPE,
+	ATTACH_TYPE,
+
 	ALL_STATS_CNT,
-	NUM_STATS_CNT = FILE_NAME - VERDICT,
+	NUM_STATS_CNT = ATTACH_TYPE - VERDICT + 1,
 };
 
 /* In comparison mode each stat can specify up to four different values:
@@ -640,19 +646,22 @@  static int append_filter_file(const char *path)
 }
 
 static const struct stat_specs default_output_spec = {
-	.spec_cnt = 7,
+	.spec_cnt = 12,
 	.ids = {
 		FILE_NAME, PROG_NAME, VERDICT, DURATION,
-		TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
+		TOTAL_INSNS, TOTAL_STATES, PEAK_STATES, SIZE,
+		JITED_SIZE, PROG_TYPE, ATTACH_TYPE, STACK,
 	},
 };
 
 static const struct stat_specs default_csv_output_spec = {
-	.spec_cnt = 9,
+	.spec_cnt = 14,
 	.ids = {
 		FILE_NAME, PROG_NAME, VERDICT, DURATION,
 		TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
 		MAX_STATES_PER_INSN, MARK_READ_MAX_LEN,
+		SIZE, JITED_SIZE, PROG_TYPE, ATTACH_TYPE,
+		STACK,
 	},
 };
 
@@ -688,6 +697,11 @@  static struct stat_def {
 	[PEAK_STATES] = { "Peak states", {"peak_states"}, },
 	[MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
 	[MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
+	[SIZE] = { "Prog size", {"prog_size", "size"}, },
+	[JITED_SIZE] = { "Jited size", {"jited_size"}, },
+	[STACK] = {"Stack depth", {"stack_depth", "stack"}, },
+	[PROG_TYPE] = { "Program type", {"program_type", "prog_type"}, },
+	[ATTACH_TYPE] = { "Attach type", {"attach_type", }, },
 };
 
 static bool parse_stat_id_var(const char *name, size_t len, int *id,
@@ -853,13 +867,16 @@  static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
 
 		if (1 == sscanf(cur, "verification time %ld usec\n", &s->stats[DURATION]))
 			continue;
-		if (6 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
+		if (5 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
 				&s->stats[TOTAL_INSNS],
 				&s->stats[MAX_STATES_PER_INSN],
 				&s->stats[TOTAL_STATES],
 				&s->stats[PEAK_STATES],
 				&s->stats[MARK_READ_MAX_LEN]))
 			continue;
+
+		if (1 == sscanf(cur, "stack depth %ld", &s->stats[STACK]))
+			continue;
 	}
 
 	return 0;
@@ -1146,8 +1163,11 @@  static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
 	char *buf;
 	int buf_sz, log_level;
 	struct verif_stats *stats;
+	struct bpf_prog_info info = {};
+	__u32 info_len = sizeof(info);
 	int err = 0;
 	void *tmp;
+	int fd;
 
 	if (!should_process_file_prog(base_filename, bpf_program__name(prog))) {
 		env.progs_skipped++;
@@ -1196,6 +1216,13 @@  static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
 	stats->file_name = strdup(base_filename);
 	stats->prog_name = strdup(bpf_program__name(prog));
 	stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
+	stats->stats[SIZE] = bpf_program__insn_cnt(prog);
+	stats->stats[PROG_TYPE] = bpf_program__type(prog);
+	stats->stats[ATTACH_TYPE] = bpf_program__expected_attach_type(prog);
+	fd = bpf_program__fd(prog);
+	if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
+		stats->stats[JITED_SIZE] = info.jited_prog_len;
+
 	parse_verif_log(buf, buf_sz, stats);
 
 	if (env.verbose) {
@@ -1309,6 +1336,11 @@  static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
 	case PROG_NAME:
 		cmp = strcmp(s1->prog_name, s2->prog_name);
 		break;
+	case ATTACH_TYPE:
+	case PROG_TYPE:
+	case SIZE:
+	case JITED_SIZE:
+	case STACK:
 	case VERDICT:
 	case DURATION:
 	case TOTAL_INSNS:
@@ -1523,12 +1555,21 @@  static void prepare_value(const struct verif_stats *s, enum stat_id id,
 		else
 			*str = s->stats[VERDICT] ? "success" : "failure";
 		break;
+	case ATTACH_TYPE:
+		*str = s ? libbpf_bpf_attach_type_str(s->stats[ATTACH_TYPE]) ? : "N/A" : "N/A";
+		break;
+	case PROG_TYPE:
+		*str = s ? libbpf_bpf_prog_type_str(s->stats[PROG_TYPE]) ? : "N/A" : "N/A";
+		break;
 	case DURATION:
 	case TOTAL_INSNS:
 	case TOTAL_STATES:
 	case PEAK_STATES:
 	case MAX_STATES_PER_INSN:
 	case MARK_READ_MAX_LEN:
+	case STACK:
+	case SIZE:
+	case JITED_SIZE:
 		*val = s ? s->stats[id] : 0;
 		break;
 	default: