Re: [RFC hid v1 01/10] bpftool: generate json output of skeletons
From: Andrii Nakryiko <hidden>
Date: 2022-12-01 18:21:24
Also in:
bpf, linux-input, lkml
On Thu, Dec 1, 2022 at 6:23 AM Benjamin Tissoires [off-list ref] wrote:
On Thu, Dec 1, 2022 at 12:06 AM Andrii Nakryiko [off-list ref] wrote:quoted
On Thu, Nov 24, 2022 at 7:16 AM Benjamin Tissoires [off-list ref] wrote:quoted
So we can then build light skeletons with loaders in any language.It would be useful to include an example generated JSON. Other than that the overall idea makes sense. Kind of machine-friendly "BPF object schema" to allow automation.Great :) I'll add the generated example in v2 then. However, I have problems figuring out how I can hit the "codegen_datasecs()" path. I think that's the only code path I am not duplicating from the do_skeleton() function, but I don't know what kind of .bpf.c program will trigger that part.
You'll hit this whenever you have global variables in your .bpf.c code. It's very important part, so let's make sure it is covered. Try using both `const volatile` read-only variables (they will end up in .rodata datasec) and global non-read-only variables (they will go into .data, if initialized to non-zero defaults, or .bss if zero-initialized).
Also, should I add some tests for it in tools/testing/selftests/bpf?
Yes, it probably makes sense to check expected JSON output is generated for a few example BPF object files.
Cheers, Benjaminquoted
quoted
Signed-off-by: Benjamin Tissoires <redacted> --- tools/bpf/bpftool/gen.c | 95 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+)diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index cf8b4e525c88..818a5209b3ac 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c@@ -904,6 +904,96 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li } } +static int gen_json(struct bpf_object *obj, const char *obj_name, size_t file_sz, uint8_t *obj_data) +{ + struct bpf_program *prog; + struct bpf_map *map; + char ident[256]; + + jsonw_start_object(json_wtr); /* root object */ + + jsonw_string_field(json_wtr, "name", obj_name); + + jsonw_bool_field(json_wtr, "use_loader", use_loader); + + /* print all maps */ + jsonw_name(json_wtr, "maps"); + jsonw_start_array(json_wtr); + bpf_object__for_each_map(map, obj) { + if (!get_map_ident(map, ident, sizeof(ident))) { + p_err("ignoring unrecognized internal map '%s'...", + bpf_map__name(map)); + continue; + } + + jsonw_start_object(json_wtr); /* map object */ + jsonw_string_field(json_wtr, "ident", ident); + jsonw_string_field(json_wtr, "name", bpf_map__name(map)); + + /* print mmap data value */ + if (is_internal_mmapable_map(map, ident, sizeof(ident))) { + const void *mmap_data = NULL; + size_t mmap_size = 0; + + mmap_data = bpf_map__initial_value(map, &mmap_size); + + jsonw_uint_field(json_wtr, "size", mmap_size); + jsonw_uint_field(json_wtr, "mmap_sz", bpf_map_mmap_sz(map)); + jsonw_name(json_wtr, "data"); + print_hex_data_json((uint8_t *)mmap_data, mmap_size); + + } + jsonw_end_object(json_wtr); /* map object */ + } + jsonw_end_array(json_wtr); + + /* print all progs */ + jsonw_name(json_wtr, "progs"); + jsonw_start_array(json_wtr); + bpf_object__for_each_program(prog, obj) { + jsonw_start_object(json_wtr); /* prog object */ + jsonw_string_field(json_wtr, "name", bpf_program__name(prog)); + jsonw_string_field(json_wtr, "sec", bpf_program__section_name(prog)); + jsonw_end_object(json_wtr); /* prog object */ + } + jsonw_end_array(json_wtr); + + /* print object data */ + if (use_loader) { + DECLARE_LIBBPF_OPTS(gen_loader_opts, opts); + int err = 0; + + err = bpf_object__gen_loader(obj, &opts); + if (err) + return err; + + err = bpf_object__load(obj); + if (err) { + p_err("failed to load object file"); + return err; + } + /* If there was no error during load then gen_loader_opts + * are populated with the loader program. + */ + + jsonw_uint_field(json_wtr, "data_sz", opts.data_sz); + jsonw_name(json_wtr, "data"); + print_hex_data_json((uint8_t *)opts.data, opts.data_sz); + + jsonw_uint_field(json_wtr, "insns_sz", opts.insns_sz); + jsonw_name(json_wtr, "insns"); + print_hex_data_json((uint8_t *)opts.insns, opts.insns_sz); + + } else { + jsonw_name(json_wtr, "data"); + print_hex_data_json(obj_data, file_sz); + } + + jsonw_end_object(json_wtr); /* root object */ + + return 0; +} + static int do_skeleton(int argc, char **argv) { char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];@@ -986,6 +1076,11 @@ static int do_skeleton(int argc, char **argv) goto out; } + if (json_output) { + err = gen_json(obj, obj_name, file_sz, (uint8_t *)obj_data); + goto out; + } + bpf_object__for_each_map(map, obj) { if (!get_map_ident(map, ident, sizeof(ident))) { p_err("ignoring unrecognized internal map '%s'...", --2.38.1