2025-05-07 13:30 UTC-0700 ~ Ihor Solodrai <isolodrai@xxxxxxxx> > bpf_doc.py parses bpf.h header to collect information about various > API elements (such as BPF helpers) and then dump them in one of the > supported formats: rst docs and a C header. > > It's useful for external tools to be able to consume this information > in an easy-to-parse format such as JSON. Implement JSON printers and > add --json command line argument. > > v2->v3: nit cleanup > v1->v2: add json printer for syscall target > > v2: https://lore.kernel.org/bpf/20250507182802.3833349-1-isolodrai@xxxxxxxx/ > v1: https://lore.kernel.org/bpf/20250506000605.497296-1-isolodrai@xxxxxxxx/ > > Signed-off-by: Ihor Solodrai <isolodrai@xxxxxxxx> > --- > scripts/bpf_doc.py | 111 ++++++++++++++++++++++++++++++++++++++------- > 1 file changed, 95 insertions(+), 16 deletions(-) > > diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py > index e74a01a85070..b157fab016a3 100755 > --- a/scripts/bpf_doc.py > +++ b/scripts/bpf_doc.py > @@ -8,6 +8,7 @@ > from __future__ import print_function > > import argparse > +import json > import re > import sys, os > import subprocess > @@ -43,6 +44,14 @@ class APIElement(object): > self.ret = ret > self.attrs = attrs > > + def to_dict(self): > + return { > + 'proto': self.proto, > + 'desc': self.desc, > + 'ret': self.ret, > + 'attrs': self.attrs > + } > + > > class Helper(APIElement): > """ > @@ -81,6 +90,11 @@ class Helper(APIElement): > > return res > > + def to_dict(self): > + d = super().to_dict() > + d.update(self.proto_break_down()) > + return d > + > > ATTRS = { > '__bpf_fastcall': 'bpf_fastcall' > @@ -675,7 +689,7 @@ COMMANDS > self.print_elem(command) > > > -class PrinterHelpers(Printer): > +class PrinterHelpersHeader(Printer): > """ > A printer for dumping collected information about helpers as C header to > be included from BPF program. > @@ -896,6 +910,43 @@ class PrinterHelpers(Printer): > print(') = (void *) %d;' % helper.enum_val) > print('') > > + > +class PrinterHelpersJSON(Printer): > + """ > + A printer for dumping collected information about helpers as a JSON file. > + @parser: A HeaderParser with Helper objects > + """ > + > + def __init__(self, parser): > + self.elements = parser.helpers > + self.elem_number_check( > + parser.desc_unique_helpers, > + parser.define_unique_helpers, > + "helper", > + "___BPF_FUNC_MAPPER", > + ) > + > + def print_all(self): > + helper_dicts = [helper.to_dict() for helper in self.elements] > + out_dict = {'helpers': helper_dicts} > + print(json.dumps(out_dict, indent=4)) > + > + > +class PrinterSyscallJSON(Printer): > + """ > + A printer for dumping collected syscall information as a JSON file. > + @parser: A HeaderParser with APIElement objects > + """ > + > + def __init__(self, parser): > + self.elements = parser.commands > + self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd') > + > + def print_all(self): > + syscall_dicts = [syscall.to_dict() for syscall in self.elements] Looks good and works well, thanks! I tested it and have one more nit: for syscalls, with JSON, we print an empty "attrs" array for each syscall subcommand, because the attributes are used with helpers only. Could we avoid printing these, please? We could refactor the attribute parsing code to make it apply to helpers only, but the easiest approach is probably to just discard the attributes from JSON right here, before we print the dict: # Attributes do not apply to syscall subcommands docs for syscall in syscall_dicts: del syscall['attrs'] > + out_dict = {'syscall': syscall_dicts} > + print(json.dumps(out_dict, indent=4)) > + > ############################################################################### >