* [PATCH] bpftool: fix line info in multi-function CFG dumps
@ 2026-10-24 6:23 Eduard Zateev
2026-10-24 6:38 ` sashiko-bot
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Eduard Zateev @ 2026-10-24 6:23 UTC (permalink / raw)
To: bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, shuah, linux-kernel,
linux-kselftest, Eduard Zateev
The CFG dumper computes BTF line-info offsets relative to each function, but bpf_prog_linfo__lfind() expects offsets from the start of the whole program. As a result, line annotations in later functions can resolve to source lines from the first function.
Pass the program instruction base to the graph dumper and use the global instruction offset for line-info lookup and display. Keep function-info matching relative to the function as before.
Add a selftest with a BPF-to-BPF call that checks the second function source line in visual output.
Link: https://github.com/libbpf/bpftool/issues/178
Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com>
---
tools/bpf/bpftool/cfg.c | 19 +++++----
tools/bpf/bpftool/xlated_dumper.c | 10 +++--
tools/bpf/bpftool/xlated_dumper.h | 3 +-
.../selftests/bpf/prog_tests/bpftool_cfg.c | 41 +++++++++++++++++++
.../testing/selftests/bpf/progs/bpftool_cfg.c | 16 ++++++++
5 files changed, 75 insertions(+), 14 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
create mode 100644 tools/testing/selftests/bpf/progs/bpftool_cfg.c
diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
index e3785f9a6..65ec45469 100644
--- a/tools/bpf/bpftool/cfg.c
+++ b/tools/bpf/bpftool/cfg.c
@@ -383,7 +383,7 @@ static void cfg_destroy(struct cfg *cfg)
static void
draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
- bool opcodes, bool linum)
+ struct bpf_insn *prog_start, bool opcodes, bool linum)
{
const char *shape;
@@ -400,11 +400,11 @@ draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
} else if (bb->idx == EXIT_BLOCK_INDEX) {
printf("EXIT");
} else {
- unsigned int start_idx;
+ unsigned int start_idx = bb->head - func->start;
+
printf("{\\\n");
- start_idx = bb->head - func->start;
dump_xlated_for_graph(dd, bb->head, bb->tail, start_idx,
- opcodes, linum);
+ prog_start, opcodes, linum);
printf("}");
}
@@ -432,12 +432,12 @@ static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb)
static void
func_output_bb_def(struct func_node *func, struct dump_data *dd,
- bool opcodes, bool linum)
+ struct bpf_insn *prog_start, bool opcodes, bool linum)
{
struct bb_node *bb;
list_for_each_entry(bb, &func->bbs, l) {
- draw_bb_node(func, bb, dd, opcodes, linum);
+ draw_bb_node(func, bb, dd, prog_start, opcodes, linum);
}
}
@@ -458,7 +458,8 @@ static void func_output_edges(struct func_node *func)
}
static void
-cfg_dump(struct cfg *cfg, struct dump_data *dd, bool opcodes, bool linum)
+cfg_dump(struct cfg *cfg, struct dump_data *dd, struct bpf_insn *prog_start,
+ bool opcodes, bool linum)
{
struct func_node *func;
@@ -466,7 +467,7 @@ cfg_dump(struct cfg *cfg, struct dump_data *dd, bool opcodes, bool linum)
list_for_each_entry(func, &cfg->funcs, l) {
printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n",
func->idx, func->idx);
- func_output_bb_def(func, dd, opcodes, linum);
+ func_output_bb_def(func, dd, prog_start, opcodes, linum);
func_output_edges(func);
printf("}\n");
}
@@ -483,7 +484,7 @@ void dump_xlated_cfg(struct dump_data *dd, void *buf, unsigned int len,
if (cfg_build(&cfg, insn, len))
return;
- cfg_dump(&cfg, dd, opcodes, linum);
+ cfg_dump(&cfg, dd, insn, opcodes, linum);
cfg_destroy(&cfg);
}
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index 5579173a6..847241bbe 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -353,7 +353,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
}
void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
- unsigned int start_idx,
+ unsigned int start_idx, struct bpf_insn *prog_start,
bool opcodes, bool linum)
{
const struct bpf_insn_cbs cbs = {
@@ -374,6 +374,7 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
for (; cur <= insn_end; cur++) {
unsigned int insn_off;
+ unsigned int linfo_offset;
if (double_insn) {
double_insn = false;
@@ -381,7 +382,8 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
}
double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
- insn_off = (unsigned int)(cur - insn_start + start_idx);
+ insn_off = (unsigned int)(cur - insn_start) + start_idx;
+ linfo_offset = (unsigned int)(cur - prog_start);
if (btf && record) {
if (record->insn_off == insn_off) {
btf_dumper_type_only(btf, record->type_id,
@@ -396,14 +398,14 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
if (prog_linfo) {
const struct bpf_line_info *linfo;
- linfo = bpf_prog_linfo__lfind(prog_linfo, insn_off, 0);
+ linfo = bpf_prog_linfo__lfind(prog_linfo, linfo_offset, 0);
if (linfo && linfo != last_linfo) {
btf_dump_linfo_dotlabel(btf, linfo, linum);
last_linfo = linfo;
}
}
- printf("%u: ", insn_off);
+ printf("%u: ", linfo_offset);
print_bpf_insn(&cbs, cur, true);
printf("\\l\\\n");
diff --git a/tools/bpf/bpftool/xlated_dumper.h b/tools/bpf/bpftool/xlated_dumper.h
index db3ba0671..d2af7bd10 100644
--- a/tools/bpf/bpftool/xlated_dumper.h
+++ b/tools/bpf/bpftool/xlated_dumper.h
@@ -8,6 +8,7 @@
#define MODULE_MAX_NAME 64
struct bpf_prog_linfo;
+struct bpf_insn;
struct kernel_sym {
unsigned long address;
@@ -36,7 +37,7 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum);
void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
- unsigned int start_index,
+ unsigned int start_idx, struct bpf_insn *prog_start,
bool opcodes, bool linum);
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
new file mode 100644
index 000000000..7d5b80927
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <bpftool_helpers.h>
+#include <test_progs.h>
+#include <linux/bpf.h>
+#include "bpftool_cfg.skel.h"
+
+#define MAX_BPFTOOL_OUTPUT_LEN (64 * 1024)
+
+void test_bpftool_cfg(void)
+{
+ struct bpftool_cfg *skel = NULL;
+ struct bpf_prog_info info = {};
+ __u32 info_len = sizeof(info);
+ char output[MAX_BPFTOOL_OUTPUT_LEN];
+ char cmd[MAX_BPFTOOL_CMD_LEN];
+ const char *expected = "return\\ bpf_ktime_get_ns()\\ ?\\ skb->len\\ :\\ 0;";
+ int err;
+
+ skel = bpftool_cfg__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "bpftool_cfg__open_and_load"))
+ return;
+
+ err = bpf_obj_get_info_by_fd(bpf_program__fd(skel->progs.bpftool_cfg),
+ &info, &info_len);
+ if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd"))
+ goto cleanup;
+
+ err = snprintf(cmd, sizeof(cmd), "prog dump xlated id %u visual linum",
+ info.id);
+ if (!ASSERT_GT(err, 0, "format bpftool command"))
+ goto cleanup;
+
+ err = get_bpftool_command_output(cmd, output, sizeof(output));
+ if (ASSERT_OK(err, "dump program CFG")) {
+ ASSERT_HAS_SUBSTR(output, "cluster_1", "second function in CFG");
+ ASSERT_HAS_SUBSTR(output, expected, "second function line info");
+ }
+
+cleanup:
+ bpftool_cfg__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/bpftool_cfg.c b/tools/testing/selftests/bpf/progs/bpftool_cfg.c
new file mode 100644
index 000000000..db01f20c8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/bpftool_cfg.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+static __noinline int helper(struct __sk_buff *skb)
+{
+ return bpf_ktime_get_ns() ? skb->len : 0;
+}
+
+SEC("socket")
+int bpftool_cfg(struct __sk_buff *skb)
+{
+ return helper(skb);
+}
+
+char _license[] SEC("license") = "GPL";
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] bpftool: fix line info in multi-function CFG dumps
2026-10-24 6:23 [PATCH] bpftool: fix line info in multi-function CFG dumps Eduard Zateev
@ 2026-10-24 6:38 ` sashiko-bot
2026-10-24 7:05 ` bot+bpf-ci
2026-10-26 10:58 ` Quentin Monnet
2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-24 6:38 UTC (permalink / raw)
To: Eduard Zateev, bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpftool: function-relative bpf_func_info offset in dump_xlated_for_graph()
--
commit 880c182e492fbe51e0cdd7cbcdc7fcfdb2fe9c94
Author: Eduard Zateev <hackerowskiy@gmail.com>
bpftool: fix line info in multi-function CFG dumps
This commit passes the program start pointer into the CFG graph dumper so that
BTF line-info lookups and displayed instruction indices use the global
instruction offset rather than a function-relative offset, while keeping
function-info matching relative to each function. It also adds a BPF selftest
for multi-function visual CFG output.
Link: https://github.com/libbpf/bpftool/issues/178
> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
> index e3785f9a697de..65ec45469743b 100644
> --- a/tools/bpf/bpftool/cfg.c
> +++ b/tools/bpf/bpftool/cfg.c
> @@ -383,7 +383,7 @@ static void cfg_destroy(struct cfg *cfg)
>
> static void
> draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
> - bool opcodes, bool linum)
> + struct bpf_insn *prog_start, bool opcodes, bool linum)
> {
> const char *shape;
>
> @@ -400,11 +400,11 @@ draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
> } else if (bb->idx == EXIT_BLOCK_INDEX) {
> printf("EXIT");
> } else {
> - unsigned int start_idx;
> + unsigned int start_idx = bb->head - func->start;
> +
> printf("{\\\n");
> - start_idx = bb->head - func->start;
> dump_xlated_for_graph(dd, bb->head, bb->tail, start_idx,
> - opcodes, linum);
> + prog_start, opcodes, linum);
> printf("}");
> }
>
[ ... ]
> diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
> index 5579173a61e3b..847241bbe91e2 100644
> --- a/tools/bpf/bpftool/xlated_dumper.c
> +++ b/tools/bpf/bpftool/xlated_dumper.c
> @@ -353,7 +353,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
> }
>
> void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
> - unsigned int start_idx,
> + unsigned int start_idx, struct bpf_insn *prog_start,
> bool opcodes, bool linum)
> {
> const struct bpf_insn_cbs cbs = {
> @@ -374,6 +374,7 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
>
> for (; cur <= insn_end; cur++) {
> unsigned int insn_off;
> + unsigned int linfo_offset;
>
> if (double_insn) {
> double_insn = false;
> @@ -381,7 +382,8 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
> }
> double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
>
> - insn_off = (unsigned int)(cur - insn_start + start_idx);
> + insn_off = (unsigned int)(cur - insn_start) + start_idx;
> + linfo_offset = (unsigned int)(cur - prog_start);
> if (btf && record) {
> if (record->insn_off == insn_off) {
[Severity: Medium]
Should struct bpf_func_info matching in dump_xlated_for_graph() also use the
global instruction offset from the start of the program rather than the
function-relative insn_off?
In the kernel verifier and in dump_xlated_plain(), record->insn_off is the
global instruction offset from the start of the whole program (0 for the main
program, and the subprogram start offset for each subprogram).
In dump_xlated_for_graph(), record is initialized to dd->func_info at the
start of each basic block:
struct bpf_func_info *record = dd->func_info;
...
insn_off = (unsigned int)(cur - insn_start) + start_idx;
linfo_offset = (unsigned int)(cur - prog_start);
if (btf && record) {
if (record->insn_off == insn_off) {
btf_dumper_type_only(btf, record->type_id,
func_sig,
sizeof(func_sig));
if (func_sig[0] != '\0')
printf("; %s:\\l\\\n", func_sig);
record = (void *)record + dd->finfo_rec_size;
}
}
Because draw_bb_node() computes start_idx as bb->head - func->start, every
subprogram's entry basic block starts at insn_off == 0. When
dump_xlated_cfg() -> cfg_dump() -> func_output_bb_def() -> draw_bb_node() ->
dump_xlated_for_graph() runs on the second function (cluster_1), insn_off == 0
matches func_info[0] (record->insn_off == 0) and prints the first function's
prototype at the entry of the second function.
Then record advances to func_info[1], whose record->insn_off is the global
instruction offset of the second function (for example, 2 in bpftool_cfg.c).
When the loop reaches function-relative insn_off == 2 inside the second
function's entry block (global instruction 4), record->insn_off == insn_off
matches mid-block and emits the second function's prototype before instruction
4 instead of instruction 2 (or omits it if the entry basic block is shorter
than the global offset).
If draw_bb_node() passes start_idx as bb->head - prog_start (or if
dump_xlated_for_graph() computes the global offset from prog_start and walks
dd->func_info up to that offset), wouldn't that fix the function prototype
annotations in cluster_1 as well and avoid needing both start_idx and
prog_start?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261024062329.264086-1-hackerowskiy@gmail.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] bpftool: fix line info in multi-function CFG dumps
2026-10-24 6:23 [PATCH] bpftool: fix line info in multi-function CFG dumps Eduard Zateev
2026-10-24 6:38 ` sashiko-bot
@ 2026-10-24 7:05 ` bot+bpf-ci
2026-10-26 10:58 ` Quentin Monnet
2 siblings, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-10-24 7:05 UTC (permalink / raw)
To: hackerowskiy, bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, shuah, linux-kernel,
linux-kselftest, hackerowskiy, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3172 bytes --]
> bpftool: fix line info in multi-function CFG dumps
TL;DR:
- Selftest's expected string likely never matches: it doesn't escape '>' in
"skb->len", unlike the DOT output
- Even if fixed, the check may not distinguish fixed from old code
> diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
> new file mode 100644
> index 000000000000..7d5b80927b65
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
> @@ -0,0 +1,41 @@
[ ... ]
> +void test_bpftool_cfg(void)
> +{
> + struct bpftool_cfg *skel = NULL;
> + struct bpf_prog_info info = {};
> + __u32 info_len = sizeof(info);
> + char output[MAX_BPFTOOL_OUTPUT_LEN];
> + char cmd[MAX_BPFTOOL_CMD_LEN];
> + const char *expected = "return\\ bpf_ktime_get_ns()\\ ?\\ skb->len\\ :\\ 0;";
> + int err;
Can this expected string ever match the bpftool output?
The DOT output prints source lines through btf_dump_linfo_dotlabel() ->
dotlabel_puts(), which escapes '>' as well as ' ':
static void dotlabel_puts(const char *s)
{
for (; *s; ++s) {
switch (*s) {
...
case '<':
case '>':
case '|':
case ' ':
putchar('\\');
So the line "return bpf_ktime_get_ns() ? skb->len : 0;" is printed as:
return\ bpf_ktime_get_ns()\ ?\ skb-\>len\ :\ 0;
The expected string escapes the spaces but not the '>', so it contains
"skb->len" where the output has "skb-\>len". Would the string need to be
"skb-\\>len" for the strstr() in ASSERT_HAS_SUBSTR() to find it?
As far as I can tell, no other part of the visual output contains this source
text unescaped, since print_insn_for_graph() escapes '>' as well.
[ ... ]
> + err = get_bpftool_command_output(cmd, output, sizeof(output));
> + if (ASSERT_OK(err, "dump program CFG")) {
> + ASSERT_HAS_SUBSTR(output, "cluster_1", "second function in CFG");
> + ASSERT_HAS_SUBSTR(output, expected, "second function line info");
Even with the escaping corrected, can this check tell the fixed code from the
old code?
In this program the main function is only two instructions (call; exit) and
helper() is longer. With the old function-relative lookup, the basic blocks
of helper() at relative index 2 or higher already resolve to helper()'s own
line info records, because their global insn_off is also 2 or higher, and
those records all point at this same source line:
return bpf_ktime_get_ns() ? skb->len : 0;
So that line probably appears in cluster_1 before the fix as well.
The commit message says the selftest "checks the second function source line
in visual output". Would it discriminate better to check that the main
function's line, "return\ helper(skb);", does not appear in cluster_1, or that
the helper() line comes right before the first instruction of the cluster?
> + }
> +
> +cleanup:
> + bpftool_cfg__destroy(skel);
> +}
---
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/40071369261
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] bpftool: fix line info in multi-function CFG dumps
2026-10-24 6:23 [PATCH] bpftool: fix line info in multi-function CFG dumps Eduard Zateev
2026-10-24 6:38 ` sashiko-bot
2026-10-24 7:05 ` bot+bpf-ci
@ 2026-10-26 10:58 ` Quentin Monnet
2 siblings, 0 replies; 4+ messages in thread
From: Quentin Monnet @ 2026-10-26 10:58 UTC (permalink / raw)
To: Eduard Zateev, bpf
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, shuah, linux-kernel,
linux-kselftest
2026-10-24 08:23 UTC+0200 ~ Eduard Zateev <hackerowskiy@gmail.com>
> The CFG dumper computes BTF line-info offsets relative to each function, but bpf_prog_linfo__lfind() expects offsets from the start of the whole program. As a result, line annotations in later functions can resolve to source lines from the first function.
>
> Pass the program instruction base to the graph dumper and use the global instruction offset for line-info lookup and display. Keep function-info matching relative to the function as before.
Please wrap your commit description. Also capitalise your title , and
target your patch to the bpf-next tree ("[PATCH bpf-next v2] bpftool:
Fix line info...")
> Add a selftest with a BPF-to-BPF call that checks the second function source line in visual output.
>
> Link: https://github.com/libbpf/bpftool/issues/178
Thanks for picking up this issue. Since you found that page, you must
also have seen the patch I posted there, and on which yours is based, so
some crediting would be appreciated.
> Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com>
> ---
> tools/bpf/bpftool/cfg.c | 19 +++++----
> tools/bpf/bpftool/xlated_dumper.c | 10 +++--
> tools/bpf/bpftool/xlated_dumper.h | 3 +-
> .../selftests/bpf/prog_tests/bpftool_cfg.c | 41 +++++++++++++++++++
> .../testing/selftests/bpf/progs/bpftool_cfg.c | 16 ++++++++
> 5 files changed, 75 insertions(+), 14 deletions(-)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
> create mode 100644 tools/testing/selftests/bpf/progs/bpftool_cfg.c
>
> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
> diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
> diff --git a/tools/bpf/bpftool/xlated_dumper.h b/tools/bpf/bpftool/xlated_dumper.h
The fix itself looks good to me.
> diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
> new file mode 100644
> index 000000000..7d5b80927
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <bpftool_helpers.h>
> +#include <test_progs.h>
> +#include <linux/bpf.h>
> +#include "bpftool_cfg.skel.h"
> +
> +#define MAX_BPFTOOL_OUTPUT_LEN (64 * 1024)
> +
> +void test_bpftool_cfg(void)
> +{
> + struct bpftool_cfg *skel = NULL;
> + struct bpf_prog_info info = {};
> + __u32 info_len = sizeof(info);
> + char output[MAX_BPFTOOL_OUTPUT_LEN];
> + char cmd[MAX_BPFTOOL_CMD_LEN];
> + const char *expected = "return\\ bpf_ktime_get_ns()\\ ?\\ skb->len\\ :\\ 0;";
How did you test this? I would expect bpftool to print "skb-\>len" and
not "skb->len", so the expected string should never be found?
> + int err;
> +
> + skel = bpftool_cfg__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "bpftool_cfg__open_and_load"))
> + return;
> +
> + err = bpf_obj_get_info_by_fd(bpf_program__fd(skel->progs.bpftool_cfg),
> + &info, &info_len);
> + if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd"))
> + goto cleanup;
> +
> + err = snprintf(cmd, sizeof(cmd), "prog dump xlated id %u visual linum",
> + info.id);
> + if (!ASSERT_GT(err, 0, "format bpftool command"))
> + goto cleanup;
> +
> + err = get_bpftool_command_output(cmd, output, sizeof(output));
> + if (ASSERT_OK(err, "dump program CFG")) {
> + ASSERT_HAS_SUBSTR(output, "cluster_1", "second function in CFG");
Escaping issue aside, I don't think this check would catch the issue in
the first place? The string is present in bpftool's output regardless of
your fix. Maybe check that "return\ helper(skb);" does not appear after
the main block, instead? And please make sure to test it.
pw-bot: cr
> + ASSERT_HAS_SUBSTR(output, expected, "second function line info");
> + }
> +
> +cleanup:
> + bpftool_cfg__destroy(skel);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/bpftool_cfg.c b/tools/testing/selftests/bpf/progs/bpftool_cfg.c
Nit: You're not doing a generic validation of the CFG from bpftool here,
so I think the names for the new test files should be more descriptive
to better indicate what the test it about ("bpftool_cfg_func_offset.c"?)
> new file mode 100644
> index 000000000..db01f20c8
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/bpftool_cfg.c
> @@ -0,0 +1,16 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +
> +static __noinline int helper(struct __sk_buff *skb)
> +{
> + return bpf_ktime_get_ns() ? skb->len : 0;
> +}
> +
> +SEC("socket")
> +int bpftool_cfg(struct __sk_buff *skb)
> +{
> + return helper(skb);
> +}
> +
> +char _license[] SEC("license") = "GPL";
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-26 10:58 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-24 6:23 [PATCH] bpftool: fix line info in multi-function CFG dumps Eduard Zateev
2026-10-24 6:38 ` sashiko-bot
2026-10-24 7:05 ` bot+bpf-ci
2026-10-26 10:58 ` Quentin Monnet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox