mbox series

[bpf-next,v2,0/5] bpftool: Add inline annotations when dumping program CFGs

Message ID 20230327110655.58363-1-quentin@isovalent.com (mailing list archive)
Headers show
Series bpftool: Add inline annotations when dumping program CFGs | expand

Message

Quentin Monnet March 27, 2023, 11:06 a.m. UTC
This set contains some improvements for bpftool's "visual" program dump
option, which produces the control flow graph in a DOT format. The main
objective is to add support for inline annotations on such graphs, so that
we can have the C source code for the program showing up alongside the
instructions, when available. The last commits also make it possible to
display the line numbers or the bare opcodes in the graph, as supported by
regular program dumps.

v2: Replace fputc(..., stdout) with putchar(...) in dotlabel_puts().

Quentin Monnet (5):
  bpftool: Fix documentation about line info display for prog dumps
  bpftool: Fix bug for long instructions in program CFG dumps
  bpftool: Support inline annotations when dumping the CFG of a program
  bpftool: Support "opcodes", "linum", "visual" simultaneously
  bpftool: Support printing opcodes and source file references in CFG

 .../bpftool/Documentation/bpftool-prog.rst    | 18 ++---
 tools/bpf/bpftool/bash-completion/bpftool     | 18 +++--
 tools/bpf/bpftool/btf_dumper.c                | 49 ++++++++++++
 tools/bpf/bpftool/cfg.c                       | 29 +++----
 tools/bpf/bpftool/cfg.h                       |  5 +-
 tools/bpf/bpftool/main.h                      |  2 +
 tools/bpf/bpftool/prog.c                      | 78 ++++++++++---------
 tools/bpf/bpftool/xlated_dumper.c             | 52 ++++++++++++-
 tools/bpf/bpftool/xlated_dumper.h             |  3 +-
 9 files changed, 182 insertions(+), 72 deletions(-)

Comments

Eduard Zingerman March 27, 2023, 5:56 p.m. UTC | #1
On Mon, 2023-03-27 at 12:06 +0100, Quentin Monnet wrote:
> This set contains some improvements for bpftool's "visual" program dump
> option, which produces the control flow graph in a DOT format. The main
> objective is to add support for inline annotations on such graphs, so that
> we can have the C source code for the program showing up alongside the
> instructions, when available. The last commits also make it possible to
> display the line numbers or the bare opcodes in the graph, as supported by
> regular program dumps.
> 
> v2: Replace fputc(..., stdout) with putchar(...) in dotlabel_puts().

Hi Quentin,

It looks like currently there are no test cases for bpftool prog dump.
Borrowing an idea to mix bpf program with comments parsed by awk from
prog_tests/btf_dump.c it is possible to put together something like
below (although, would be much simpler as a bash script). Is it worth
the effort or dump format is too unstable?

Thanks,
Eduard

---

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..f582a93b5ee9
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_cfg.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+
+#include "bpf/libbpf.h"
+#include "bpftool_cfg.skel.h"
+
+static int system_to_tmp(char *tmp_template, char *cmd)
+{
+	char buf[1024];
+	int fd, err;
+
+	fd = mkstemp(tmp_template);
+	if (!ASSERT_GE(fd, 0, "mkstemp"))
+		return fd;
+
+	snprintf(buf, sizeof(buf), "%s > %s", cmd, tmp_template);
+	err = system(buf);
+	if (err) {
+		PRINT_FAIL("Command %s failed: err %d\n", cmd, err);
+		remove(tmp_template);
+		return err;
+	}
+
+	return 0;
+}
+
+void test_bpftool_cfg(void)
+{
+	const char *prog_pin_path = "/sys/fs/bpf/bpftool_cfg_test_pin";
+	const char *bpftool = "./tools/build/bpftool/bpftool";
+	char bpftool_tmp[256] = "/tmp/bpftool_test_cfg.XXXXXX";
+	char awk_tmp[256] = "/tmp/bpftool_test_awk.XXXXXX";
+	struct bpftool_cfg *skel;
+	const char *test_file;
+	char cmd_buf[1024];
+	FILE *cmd;
+	int err;
+
+	skel = bpftool_cfg__open_and_load();
+	if (!skel) {
+		PRINT_FAIL("failed to load bpftool_cfg program: %d (%s)\n",
+			   errno, strerror(errno));
+		return;
+	}
+
+	err = bpf_program__pin(skel->progs.bpftool_cfg_nanosleep, prog_pin_path);
+	if (err) {
+		PRINT_FAIL("failed to pin bpftool_cfg program: err %d, errno = %d (%s)\n",
+			   err, errno, strerror(errno));
+		goto out;
+	}
+
+	/* When the test is run with O=, kselftest copies TEST_FILES
+	 * without preserving the directory structure.
+	 */
+	if (access("progs/bpftool_cfg.c", R_OK) == 0)
+		test_file = "progs/bpftool_cfg.c";
+	else if (access("bpftool_cfg.c", R_OK) == 0)
+		test_file = "bpftool_cfg.c";
+	else {
+		PRINT_FAIL("Can't find bpftool_cfg.c\n");
+		goto out_unpin;
+	}
+
+	cmd = fmemopen(cmd_buf, sizeof(cmd_buf), "w");
+	fprintf(cmd, "awk '");
+	fprintf(cmd, "    /END-BPFTOOL-CFG/   { out=0 } ");
+	fprintf(cmd, "    out                 { print $0 } ");
+	fprintf(cmd, "    /START-BPFTOOL-CFG/ { out=1 } ");
+	fprintf(cmd, "' '%s' > '%s'", test_file, awk_tmp);
+	fclose(cmd);
+	err = system_to_tmp(awk_tmp, cmd_buf);
+	if (!ASSERT_OK(err, "awk"))
+		goto out_unpin;
+
+	cmd = fmemopen(cmd_buf, sizeof(cmd_buf), "w");
+	fprintf(cmd, "%s prog dump xlated pinned %s visual", bpftool, prog_pin_path);
+	fclose(cmd);
+	err = system_to_tmp(bpftool_tmp, cmd_buf);
+	if (!ASSERT_OK(err, "bpftool"))
+		goto out_delete;
+
+	cmd = fmemopen(cmd_buf, sizeof(cmd_buf), "w");
+	fprintf(cmd, "diff -u %s %s", awk_tmp, bpftool_tmp);
+	fclose(cmd);
+	err = system(cmd_buf);
+	if (!ASSERT_OK(err, "diff"))
+		goto out_delete;
+
+out_delete:
+	if (awk_tmp[0])
+		remove(awk_tmp);
+	if (bpftool_tmp[0])
+		remove(bpftool_tmp);
+out_unpin:
+	bpf_program__unpin(skel->progs.bpftool_cfg_nanosleep, prog_pin_path);
+out:
+	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 000000000000..d10c4e2cecbd
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/bpftool_cfg.c
@@ -0,0 +1,36 @@
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#include "bpf_misc.h"
+
+/*
+START-BPFTOOL-CFG
+digraph "DOT graph for eBPF program" {
+subgraph "cluster_0" {
+	style="dashed";
+	color="black";
+	label="func_0 ()";
+	fn_0_bb_0 [shape=Mdiamond,style=filled,label="ENTRY"];
+
+	fn_0_bb_2 [shape=record,style=filled,label="{\
+; int bpftool_cfg_nanosleep(void * ctx):\l\
+; return 0;\l\
+0: (b4) w0 = 0\l\
+ | 1: (95) exit\l\
+}"];
+
+	fn_0_bb_1 [shape=Mdiamond,style=filled,label="EXIT"];
+
+	fn_0_bb_0:s -> fn_0_bb_2:n [style="solid,bold", color=black, weight=10, constraint=true];
+	fn_0_bb_2:s -> fn_0_bb_1:n [style="solid,bold", color=black, weight=10, constraint=true];
+	fn_0_bb_0:s -> fn_0_bb_1:n [style="invis", constraint=true];
+}
+}
+END-BPFTOOL-CFG
+*/
+
+SEC("tp/syscalls/sys_enter_getpid")
+int bpftool_cfg_nanosleep(void *ctx)
+{
+	return 0;
+}
Quentin Monnet March 31, 2023, 2:53 p.m. UTC | #2
2023-03-27 20:56 UTC+0300 ~ Eduard Zingerman <eddyz87@gmail.com>
> On Mon, 2023-03-27 at 12:06 +0100, Quentin Monnet wrote:
>> This set contains some improvements for bpftool's "visual" program dump
>> option, which produces the control flow graph in a DOT format. The main
>> objective is to add support for inline annotations on such graphs, so that
>> we can have the C source code for the program showing up alongside the
>> instructions, when available. The last commits also make it possible to
>> display the line numbers or the bare opcodes in the graph, as supported by
>> regular program dumps.
>>
>> v2: Replace fputc(..., stdout) with putchar(...) in dotlabel_puts().
> 
> Hi Quentin,
> 
> It looks like currently there are no test cases for bpftool prog dump.
> Borrowing an idea to mix bpf program with comments parsed by awk from
> prog_tests/btf_dump.c it is possible to put together something like
> below (although, would be much simpler as a bash script). Is it worth
> the effort or dump format is too unstable?
> 
> Thanks,
> Eduard

Correct, I don't think we have tests for that at the moment.

But yes, I would love to get a test like this (and more bpftool tests in
general, when I can find cycles). I don't mind a bash script,
personally; your bpftool_cfg.c is mostly a succession of commands, and
the other tests we have for bpftool are in bash or Python anyway
(test_bpftool*). We could use bpftool to load the program (I suppose
it's debatable whether it's good to use bpftool itself to set up a test
for bpftool; on the other hand, it's heavily based on libbpf, so using
libbpf directly doesn't seem to make that much of a difference).

As for the stability of the output, generally the produced BPF bytecode
is obviously subject to change, but for a program so simple as "return
0;" we should be mostly good. Just make sure you specify the ISA version
when you compile the program, with < v3 we would load 64 bits instead of
32 in the first instruction. On bpftool's side, I don't expect the CFG
output to change much, so no problem.

Thanks a lot for your review, and for working on this test!

I'll send a v3, probably sometime next week.
Quentin