Skip to contentSkip to conversation

Improve stack depth verification stats output

PATCH Superseded Kumar Kartikeya Dwivedi 4 participants 14 messages Aug 1 – Aug 2

Subsystems (4)

v1 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Kumar Kartikeya Dwivedi

author maintainer
#0 lore raw

cover Improve stack depth verification stats output

Some improvements for more clarity in the stack depth verification statistics output. See commit logs for details.

Kumar Kartikeya Dwivedi (3):
bpf: Show more useful info in stack depth stats selftests/bpf: Adjust veristat stack depth parsing selftests/bpf: Test stack depth stats without BTF subprog names

 kernel/bpf/verifier.c                         | 16 +++++++++---
 .../bpf/progs/verifier_basic_stack.c          |  4 +--
 .../bpf/progs/verifier_bpf_fastcall.c         | 19 ++++++++------
 .../bpf/progs/verifier_private_stack.c        |  9 ++++---
 .../selftests/bpf/progs/verifier_var_off.c    |  4 +--
 tools/testing/selftests/bpf/test_verifier.c   |  5 +++-
 tools/testing/selftests/bpf/verifier/calls.c  |  4 ++-
 tools/testing/selftests/bpf/veristat.c        | 25 ++++++++++++-------
 8 files changed, 56 insertions(+), 30 deletions(-)

base-commit: 28e911d61d66b92a3bded8b54622ed3cd2795bf6

Signature · 1 line
2.53.0
author maintainer
#1 lore raw

PATCH 1/3 bpf: Show more useful info in stack depth stats

Currently, the output of stack depth statistics is two crude, with a list of captured stack depths ordered by the subprog numbers. The actual subprog numbers are determined by libbpf, hence it is hard to associate the stack depth statistic back to the subprog by name.

Change the format to:
stack depth <subprog>=<depth> ... max=<depth>

In case the subprog name is not specified, use subprog[N] as the string.

---
 kernel/bpf/verifier.c                         | 16 ++++++++++++----
 .../bpf/progs/verifier_basic_stack.c          |  4 ++--
 .../bpf/progs/verifier_bpf_fastcall.c         | 19 +++++++++++--------
 .../bpf/progs/verifier_private_stack.c        |  9 ++++++---
 .../selftests/bpf/progs/verifier_var_off.c    |  4 ++--
 5 files changed, 33 insertions(+), 19 deletions(-)
kernel/bpf/verifier.c +12 -4
index 8d0635ee48c7..51ac0a9ae339 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 	if (env->log.level & BPF_LOG_STATS) {
 		verbose(env, "verification time %lld usec\n",
 			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);
 		verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
 		for (i = 1; i < subprog_cnt; i++)
 			if (bpf_subprog_is_global(env, i))
tools/testing/selftests/bpf/progs/verifier_basic_stack.c +2 -2
index fb62e09f2114..f135c4b9344b 100644
@@ -27,7 +27,7 @@ __naked void stack_out_of_bounds(void)
 
 SEC("socket")
 __description("uninitialized stack1")
-__success __log_level(4) __msg("stack depth 8")
+__success __log_level(4) __msg("stack depth uninitialized_stack1=8")
 __failure_unpriv __msg_unpriv("invalid read from stack")
 __naked void uninitialized_stack1(void)
 {
@@ -45,7 +45,7 @@ __naked void uninitialized_stack1(void)
 
 SEC("socket")
 __description("uninitialized stack2")
-__success __log_level(4) __msg("stack depth 8")
+__success __log_level(4) __msg("stack depth uninitialized_stack2=8")
 __failure_unpriv __msg_unpriv("invalid read from stack")
 __naked void uninitialized_stack2(void)
 {
tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c +11 -8
index 83707faea049..0b48156920df 100644
@@ -10,7 +10,7 @@
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 8")
+__log_level(4) __msg("stack depth simple=8")
 __xlated("4: r5 = 5")
 __xlated("5: r0 = ")
 __xlated("6: r0 = &(void __percpu *)(r0)")
@@ -96,7 +96,7 @@ __naked void canary_zero_spills(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 16")
+__log_level(4) __msg("stack depth wrong_reg_in_pattern1=16")
 __xlated("1: *(u64 *)(r10 -16) = r1")
 __xlated("...")
 __xlated("3: r0 = &(void __percpu *)(r0)")
@@ -598,7 +598,7 @@ __naked static void subprogs_use_independent_offsets_aux(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 8")
+__log_level(4) __msg("stack depth helper_call_does_not_prevent_bpf_fastcall=8")
 __xlated("2: r0 = &(void __percpu *)(r0)")
 __success
 __naked void helper_call_does_not_prevent_bpf_fastcall(void)
@@ -620,7 +620,7 @@ __naked void helper_call_does_not_prevent_bpf_fastcall(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 24")
+__log_level(4) __msg("stack depth may_goto_interaction_x86_64=24")
 /* may_goto counter at -24 */
 __xlated("0: *(u64 *)(r10 -24) =")
 /* may_goto timestamp at -16 */
@@ -661,7 +661,7 @@ __naked void may_goto_interaction_x86_64(void)
 SEC("raw_tp")
 __arch_arm64
 __arch_riscv64
-__log_level(4) __msg("stack depth 24")
+__log_level(4) __msg("stack depth may_goto_interaction=24")
 /* may_goto counter at -24 */
 __xlated("0: *(u64 *)(r10 -24) =")
 /* may_goto timestamp at -16 */
@@ -708,7 +708,8 @@ __naked static void dummy_loop_callback(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 32+0")
+__log_level(4)
+__msg("stack depth bpf_loop_interaction1=32 dummy_loop_callback=0")
 __xlated("2: r1 = 1")
 __xlated("3: r0 =")
 __xlated("4: r0 = &(void __percpu *)(r0)")
@@ -756,7 +757,8 @@ __naked int bpf_loop_interaction1(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 40+0")
+__log_level(4)
+__msg("stack depth bpf_loop_interaction2=40 dummy_loop_callback=0")
 /* call bpf_get_smp_processor_id */
 __xlated("2: r1 = 42")
 __xlated("3: r0 =")
@@ -800,7 +802,8 @@ __naked int bpf_loop_interaction2(void)
 
 SEC("raw_tp")
 __arch_x86_64
-__log_level(4) __msg("stack depth 512+0 max 512")
+__log_level(4)
+__msg("stack depth cumulative_stack_depth=512 cumulative_stack_depth_subprog=0 max=512")
 /* just to print xlated version when debugging */
 __xlated("r0 = &(void __percpu *)(r0)")
 __success
tools/testing/selftests/bpf/progs/verifier_private_stack.c +6 -3
index bb8206e10880..4a4a5a891f62 100644
@@ -86,7 +86,8 @@ __naked static void cumulative_stack_depth_subprog(void)
 SEC("kprobe")
 __description("Private stack, subtree > MAX_BPF_STACK")
 __success
-__log_level(4) __msg("stack depth 512+32 max 512")
+__log_level(4)
+__msg("stack depth private_stack_nested_1=512 cumulative_stack_depth_subprog=32 max=512")
 __arch_x86_64
 /* private stack fp for the main prog */
 __jited("	movabsq	$0x{{.*}}, %r9")
@@ -331,7 +332,8 @@ SEC("fentry/bpf_fentry_test9")
 __description("Private stack, async callback, potential nesting")
 __success __retval(0)
 __load_if_JITed()
-__log_level(4) __msg("stack depth 8+0+256+0 max 272")
+__log_level(4)
+__msg("stack depth private_stack_async_callback_2=8 timer_cb1=0 subprog1=256 subprog2=0 max=272")
 __arch_x86_64
 __jited("	subq	$0x100, %rsp")
 __arch_arm64
@@ -355,7 +357,8 @@ int private_stack_async_callback_2(void)
 SEC("fentry/bpf_fentry_test9")
 __description("private stack, max stack depth is private stack")
 __success
-__log_level(4) __msg("stack depth 8+256+0 max 256")
+__log_level(4)
+__msg("stack depth private_stack_max_depth=8 subprog1=256 subprog2=0 max=256")
 int private_stack_max_depth(void)
 {
 	int x = 0;
tools/testing/selftests/bpf/progs/verifier_var_off.c +2 -2
index 24cd0a763673..7fb81a954eb1 100644
@@ -198,7 +198,7 @@ __success
 /* Check that the maximum stack depth is correctly maintained according to the
  * maximum possible variable offset.
  */
-__log_level(4) __msg("stack depth 16")
+__log_level(4) __msg("stack depth stack_write_priv_vs_unpriv=16")
 __failure_unpriv
 /* Variable stack access is rejected for unprivileged.
  */
@@ -238,7 +238,7 @@ __success
 /* Check that the maximum stack depth is correctly maintained according to the
  * maximum possible variable offset.
  */
-__log_level(4) __msg("stack depth 16")
+__log_level(4) __msg("stack depth stack_write_followed_by_read=16")
 __failure_unpriv
 __msg_unpriv("R2 variable stack access prohibited for !root")
 __retval(0)
Signature · 1 line
2.53.0
#2 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The new stack depth verifier log format breaks stack metrics parsing in the `veristat` tool. --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The new stack depth verifier log format breaks stack metrics parsing in the veristat tool. --

--- Patch [1]: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats --- commit f7cb915ff02de7a02fe7b2353a1f2d77e1852496
Author: Kumar Kartikeya Dwivedi <[email protected]>

bpf: Show more useful info in stack depth stats

This commit changes the output format of stack depth statistics in the BPF verifier log. It replaces the old subprogram index-based format with a clearer name and depth format, avoiding hard-to-read sequences.

Quoted 14 lines
kernel/bpf/verifier.c +2 -4
index 8d0635ee48c7e..51ac0a9ae3394 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 	if (env->log.level & BPF_LOG_STATS) {
 		verbose(env, "verification time %lld usec\n",
 			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18717

Quoted 10 lines
kernel/bpf/verifier.c +10 -0
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);

[Severity: Medium]
Does this change break the veristat tool's parsing of stack depths?

Looking at tools/testing/selftests/bpf/veristat.c in parse_verif_log(), it appears to rely on the old format using a hardcoded string and splitting by the plus sign:

tools/testing/selftests/bpf/veristat.c:parse_verif_log() {
    ...
    if (2 == sscanf(cur, "stack depth %511s max %ld", stack, &s->stats[MAX_STACK]))
    ...
    while ((token = strtok_r(cnt++ ? NULL : stack, "+", &state))) {
        if (sscanf(token, "%d", &sub_stack) == 0)
            break;
    ...
}

Since the verifier now outputs max=%ld instead of max %ld, and the individual depths are formatted as name=depth rather than numbers separated by a plus sign, will veristat silently fail to parse STACK and MAX_STACK metrics when processing BPF objects?

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
author maintainer
#3 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

Quoted 56 lines · sashiko-bot

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The new stack depth verifier log format breaks stack metrics parsing in the veristat tool. --

--- Patch [1]: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats --- commit f7cb915ff02de7a02fe7b2353a1f2d77e1852496
Author: Kumar Kartikeya Dwivedi <[email protected]>

bpf: Show more useful info in stack depth stats

This commit changes the output format of stack depth statistics in the BPF verifier log. It replaces the old subprogram index-based format with a clearer name and depth format, avoiding hard-to-read sequences.

Quoted 14 lines
kernel/bpf/verifier.c +2 -4
index 8d0635ee48c7e..51ac0a9ae3394 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
 	if (env->log.level & BPF_LOG_STATS) {
 		verbose(env, "verification time %lld usec\n",
 			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18717

Quoted 10 lines
kernel/bpf/verifier.c +10 -0
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);

[Severity: Medium]
Does this change break the veristat tool's parsing of stack depths?

Looking at tools/testing/selftests/bpf/veristat.c in parse_verif_log(), it appears to rely on the old format using a hardcoded string and splitting by the plus sign:

tools/testing/selftests/bpf/veristat.c:parse_verif_log() {
    ...
    if (2 == sscanf(cur, "stack depth %511s max %ld", stack, &s->stats[MAX_STACK]))
    ...
    while ((token = strtok_r(cnt++ ? NULL : stack, "+", &state))) {
        if (sscanf(token, "%d", &sub_stack) == 0)
            break;
    ...
}
Quoted 5 lines

Since the verifier now outputs max=%ld instead of max %ld, and the individual depths are formatted as name=depth rather than numbers separated by a plus sign, will veristat silently fail to parse STACK and MAX_STACK metrics when processing BPF objects?

Fixed in patch 2. It is an open question whether we care about format compatibility, in that case we will need further adjustments to parse both kinds of output (for older and newer kernels).

Quoted 1 line

[ ... ]

#4 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

In reply to Kumar Kartikeya Dwivedi
This isn't a bug, but there's a typo here - "is two crude" should be "is too crude".
Quoted 6 lines

bpf: Show more useful info in stack depth stats

Currently, the output of stack depth statistics is two crude, with a list of captured stack depths ordered by the subprog numbers. The actual subprog numbers are determined by libbpf, hence it is hard to associate the stack depth statistic back to the subprog by name.

This isn't a bug, but there's a typo here - "is two crude" should be "is too crude".

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/30723212958

maintainer
#5 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

In reply to Kumar Kartikeya Dwivedi
Quoted 32 lines · Kumar Kartikeya Dwivedi

Currently, the output of stack depth statistics is two crude, with a list of captured stack depths ordered by the subprog numbers. The actual subprog numbers are determined by libbpf, hence it is hard to associate the stack depth statistic back to the subprog by name.

Change the format to:
stack depth <subprog>=<depth> ... max=<depth>

In case the subprog name is not specified, use subprog[N] as the string.

---
 kernel/bpf/verifier.c                         | 16 ++++++++++++---- .../bpf/progs/verifier_basic_stack.c          |  4 ++-- .../bpf/progs/verifier_bpf_fastcall.c         | 19 +++++++++++-------- .../bpf/progs/verifier_private_stack.c        |  9 ++++++--- .../selftests/bpf/progs/verifier_var_off.c    |  4 ++--

 5 files changed, 33 insertions(+), 19 deletions(-)
kernel/bpf/verifier.c +0 -4
index 8d0635ee48c7..51ac0a9ae339 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
  	if (env->log.level & BPF_LOG_STATS) {
  		verbose(env, "verification time %lld usec\n",
  			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18715

Quoted 10 lines
kernel/bpf/verifier.c +10 -0
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);

That would be not very convenient to parse by veristat, but I'd print every subprogram on it's own line.

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18717

Quoted 2 lines
kernel/bpf/verifier.c +2 -0
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);

I think check_max_stack_depth_subprog() needs a similar update, at the moment it reports a not very helpful error like this:

combined stack size of 34 calls is 528. Too large

Printing the offending spine would be helpful.

...

author maintainer
#6 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

Quoted 38 lines · Eduard Zingerman
Quoted 37 lines · Kumar Kartikeya Dwivedi

Currently, the output of stack depth statistics is two crude, with a list of captured stack depths ordered by the subprog numbers. The actual subprog numbers are determined by libbpf, hence it is hard to associate the stack depth statistic back to the subprog by name.

Change the format to:
stack depth <subprog>=<depth> ... max=<depth>

In case the subprog name is not specified, use subprog[N] as the string.

---
 kernel/bpf/verifier.c                         | 16 ++++++++++++---- .../bpf/progs/verifier_basic_stack.c          |  4 ++-- .../bpf/progs/verifier_bpf_fastcall.c         | 19 +++++++++++-------- .../bpf/progs/verifier_private_stack.c        |  9 ++++++--- .../selftests/bpf/progs/verifier_var_off.c    |  4 ++--

 5 files changed, 33 insertions(+), 19 deletions(-)
kernel/bpf/verifier.c +5 -4
index 8d0635ee48c7..51ac0a9ae339 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
  	if (env->log.level & BPF_LOG_STATS) {
  		verbose(env, "verification time %lld usec\n",
  			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
Quoted 9 lines

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18715

Quoted 5 lines
kernel/bpf/verifier.c +5 -0
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);

That would be not very convenient to parse by veristat, but I'd print every subprogram on it's own line.

I can do that, but let's agree on the format before I get to it. How should it look like? Prefix with stack depth every line, and print stack depth max ... as the very first line? Like so:

stack depth max N
stack depth subprog 0 <name> N
...
stack depth subprog N <name> N

?

Quoted 10 lines

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18717

Quoted 2 lines
kernel/bpf/verifier.c +2 -0
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);

I think check_max_stack_depth_subprog() needs a similar update, at the moment it reports a not very helpful error like this:

combined stack size of 34 calls is 528. Too large

Printing the offending spine would be helpful.

I'll do that in verifier errors patch set. This is just for the verification stats, that are outside its scope.

Quoted 1 line

...

maintainer
#7 lore raw

Re: [PATCH bpf-next v1 1/3] bpf: Show more useful info in stack depth stats

Quoted 53 lines · Kumar Kartikeya Dwivedi
Quoted 38 lines · Eduard Zingerman
Quoted 37 lines · Kumar Kartikeya Dwivedi

Currently, the output of stack depth statistics is two crude, with a list of captured stack depths ordered by the subprog numbers. The actual subprog numbers are determined by libbpf, hence it is hard to associate the stack depth statistic back to the subprog by name.

Change the format to:
stack depth <subprog>=<depth> ... max=<depth>

In case the subprog name is not specified, use subprog[N] as the string.

---
 kernel/bpf/verifier.c                         | 16 ++++++++++++---- .../bpf/progs/verifier_basic_stack.c          |  4 ++-- .../bpf/progs/verifier_bpf_fastcall.c         | 19 +++++++++++-------- .../bpf/progs/verifier_private_stack.c        |  9 ++++++--- .../selftests/bpf/progs/verifier_var_off.c    |  4 ++--

 5 files changed, 33 insertions(+), 19 deletions(-)
kernel/bpf/verifier.c +5 -4
index 8d0635ee48c7..51ac0a9ae339 100644
@@ -18703,10 +18703,18 @@ static void print_verification_stats(struct bpf_verifier_env *env)
  	if (env->log.level & BPF_LOG_STATS) {
  		verbose(env, "verification time %lld usec\n",
  			div_u64(env->verification_time, 1000));
-		verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
-		for (i = 1; i < subprog_cnt; i++)
-			verbose(env, "+%d", env->subprog_info[i].stack_depth);
-		verbose(env, " max %d\n", env->max_stack_depth);
+		verbose(env, "stack depth");
+		for (i = 0; i < subprog_cnt; i++) {
+			const char *name = env->subprog_info[i].name;
+
+			if (name && name[0])
Quoted 9 lines

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18715

Quoted 5 lines
kernel/bpf/verifier.c +5 -0
+				verbose(env, " %s=%d", name,
+					env->subprog_info[i].stack_depth);
+			else
+				verbose(env, " subprog[%d]=%d", i,
+					env->subprog_info[i].stack_depth);

That would be not very convenient to parse by veristat, but I'd print every subprogram on it's own line.

I can do that, but let's agree on the format before I get to it. How should it look like? Prefix with stack depth every line, and print stack depth max ... as the very first line? Like so:

Quoted 6 lines

stack depth max N
stack depth subprog 0 <name> N
...
stack depth subprog N <name> N

?

Ideally I'd drop the "stack depth " prefix after the first one and just add an indentation instead. But that would be hard for veristat to parse. So let's settle on your suggested version.

Quoted 6 lines
Quoted 6 lines

kernel/bpf/verifier.c static void print_verification_stats(struct bpf_verifier_env *env) · line 18717

Quoted 2 lines
kernel/bpf/verifier.c +2 -0
+		}
+		verbose(env, " max=%d\n", env->max_stack_depth);

I think check_max_stack_depth_subprog() needs a similar update, at the moment it reports a not very helpful error like this:

Quoted 7 lines
Quoted 4 lines

  combined stack size of 34 calls is 528. Too large

Printing the offending spine would be helpful.

I'll do that in verifier errors patch set. This is just for the verification stats, that are outside its scope.

Fine by me

author maintainer
#8 lore raw

PATCH 2/3 selftests/bpf: Adjust veristat stack depth parsing

In reply to Kumar Kartikeya Dwivedi

The verifier changed the output format for stack depth verification statistics. Adjust the parsing code.

---
 tools/testing/selftests/bpf/veristat.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
tools/testing/selftests/bpf/veristat.c +16 -9
index c9c257784ee3..f470e63df440 100644
@@ -997,9 +997,10 @@ static char verif_log_buf[64 * 1024];
 
 static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *s)
 {
-	const char *cur;
-	int pos, lines, sub_stack, cnt = 0;
-	char *state = NULL, *token, stack[512];
+	long stack_total, sub_stack;
+	const char *cur, *p;
+	int pos, lines;
+	char *end;
 
 	buf[buf_sz - 1] = '\0';
 
@@ -1025,13 +1026,19 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *
 				&s->stats[MARK_READ_MAX_LEN]))
 			continue;
 
-		if (2 == sscanf(cur, "stack depth %511s max %ld", stack, &s->stats[MAX_STACK]))
+		if (strncmp(cur, "stack depth ", sizeof("stack depth ") - 1))
 			continue;
-	}
-	while ((token = strtok_r(cnt++ ? NULL : stack, "+", &state))) {
-		if (sscanf(token, "%d", &sub_stack) == 0)
-			break;
-		s->stats[STACK] += sub_stack;
+
+		stack_total = 0;
+		for (p = cur + sizeof("stack depth ") - 1; (p = strchr(p, '=')); p = end) {
+			sub_stack = strtol(p + 1, &end, 10);
+			if (*end == '\n') {
+				s->stats[STACK] = stack_total;
+				s->stats[MAX_STACK] = sub_stack;
+				break;
+			}
+			stack_total += sub_stack;
+		}
 	}
 	return 0;
 }
Signature · 1 line
2.53.0
maintainer
#9 lore raw

Re: [PATCH bpf-next v1 2/3] selftests/bpf: Adjust veristat stack depth parsing

Quoted 5 lines · Kumar Kartikeya Dwivedi

The verifier changed the output format for stack depth verification statistics. Adjust the parsing code.

---

Do we care about backwards compatibility in veristat? E.g. I see it packaged in my debian system.
This won't be able to parse the output of an old kernel. On the other hand, I personally do not use stack metrics, so the change does not really affect me.

...

author maintainer
#10 lore raw

Re: [PATCH bpf-next v1 2/3] selftests/bpf: Adjust veristat stack depth parsing

Quoted 4 lines · Eduard Zingerman
Quoted 3 lines · Kumar Kartikeya Dwivedi

The verifier changed the output format for stack depth verification statistics. Adjust the parsing code.

Quoted 9 lines
Quoted 2 lines

---

Do we care about backwards compatibility in veristat? E.g. I see it packaged in my debian system.
This won't be able to parse the output of an old kernel. On the other hand, I personally do not use stack metrics, so the change does not really affect me.

It is an open question, given it is packaged, my instinct now is to parse both formats, to cause the last amount of disruption. So that's what I'll do unless someone tells me otherwise.

Quoted 1 line

...

author maintainer
#11 lore raw

PATCH 3/3 selftests/bpf: Test stack depth stats without BTF subprog names

In reply to Kumar Kartikeya Dwivedi

Test the case where we don't supply names for the subprogs.

---
 tools/testing/selftests/bpf/test_verifier.c  | 5 ++++-
 tools/testing/selftests/bpf/verifier/calls.c | 4 +++-
 2 files changed, 7 insertions(+), 2 deletions(-)
tools/testing/selftests/bpf/test_verifier.c +4 -1
index a8ae03c57bba..61f0b2951bcd 100644
@@ -133,6 +133,7 @@ struct bpf_test {
 	const char *errstr;
 	const char *errstr_unpriv;
 	uint32_t insn_processed;
+	uint32_t log_level;
 	int prog_len;
 	enum {
 		UNDEF,
@@ -1559,7 +1560,9 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 		       test->errstr_unpriv : test->errstr;
 
 	opts.expected_attach_type = test->expected_attach_type;
-	if (expected_ret == VERBOSE_ACCEPT)
+	if (test->log_level)
+		opts.log_level = test->log_level;
+	else if (expected_ret == VERBOSE_ACCEPT)
 		opts.log_level = 2;
 	else if (verbose)
 		opts.log_level = verif_log_level | 4; /* force stats */
tools/testing/selftests/bpf/verifier/calls.c +3 -1
index 302d712e0d7e..1f7ca271e07a 100644
@@ -1091,7 +1091,9 @@
 	/* stack_main=32, stack_A=256, stack_B=64
 	 * and max(main+A, main+A+B) < 512
 	 */
-	.result = ACCEPT,
+	.result = VERBOSE_ACCEPT,
+	.errstr = "stack depth subprog[0]=32 subprog[1]=256 subprog[2]=64 max=352",
+	.log_level = DEFAULT_LIBBPF_LOG_LEVEL,
 },
 {
 	"calls: stack depth check using three frames. test2",
Signature · 1 line
2.53.0
maintainer
#12 lore raw

Re: [PATCH bpf-next v1 3/3] selftests/bpf: Test stack depth stats without BTF subprog names

On Sun, 2026-08-02 at 01:03 +0200, Kumar Kartikeya Dwivedi wrote:

...

Quoted 9 lines
tools/testing/selftests/bpf/test_verifier.c +0 -0
@@ -1559,7 +1560,9 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
  		       test->errstr_unpriv : test->errstr;


  opts.expected_attach_type = test->expected_attach_type;

tools/testing/selftests/bpf/test_verifier.c +3 -1
-	if (expected_ret == VERBOSE_ACCEPT)
+	if (test->log_level)
+		opts.log_level = test->log_level;
+	else if (expected_ret == VERBOSE_ACCEPT)

  opts.log_level = 2;

I did this:

          else if (expected_ret == VERBOSE_ACCEPT)

- opts.log_level = 2;
+ opts.log_level = 2 | 4;

And all test_verifier tests passed.
There is no need to a new option.

Quoted 2 lines

  else if (verbose)
  opts.log_level = verif_log_level | 4; /* force stats */

...

author maintainer
#13 lore raw

Re: [PATCH bpf-next v1 3/3] selftests/bpf: Test stack depth stats without BTF subprog names

Quoted 16 lines · Eduard Zingerman

On Sun, 2026-08-02 at 01:03 +0200, Kumar Kartikeya Dwivedi wrote:

...

Quoted 9 lines
tools/testing/selftests/bpf/test_verifier.c +0 -0
@@ -1559,7 +1560,9 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
  		       test->errstr_unpriv : test->errstr;


  opts.expected_attach_type = test->expected_attach_type;

tools/testing/selftests/bpf/test_verifier.c +3 -1
-	if (expected_ret == VERBOSE_ACCEPT)
+	if (test->log_level)
+		opts.log_level = test->log_level;
+	else if (expected_ret == VERBOSE_ACCEPT)

  opts.log_level = 2;

I did this:

Quoted 7 lines
          else if (expected_ret == VERBOSE_ACCEPT)

- opts.log_level = 2;
+ opts.log_level = 2 | 4;

And all test_verifier tests passed.
There is no need to a new option.

Ack.

Quoted 4 lines
Quoted 2 lines

  else if (verbose)
  opts.log_level = verif_log_level | 4; /* force stats */

...