2022-01-11 18:44 UTC+0000 ~ Usama Arif <usama.arif@xxxxxxxxxxxxx> > Currently bpf_helper_defs.h and the bpf helpers man page are auto-generated > using function documentation present in bpf.h. If the documentation for the > helper is missing or doesn't follow a specific format for e.g. if a function > is documented as: > * long bpf_kallsyms_lookup_name( const char *name, int name_sz, int flags, u64 *res ) > instead of > * long bpf_kallsyms_lookup_name(const char *name, int name_sz, int flags, u64 *res) > (notice the extra space at the start and end of function arguments) > then that helper is not dumped in the auto-generated header and results in > an invalid call during eBPF runtime, even if all the code specific to the > helper is correct. > > This patch checks the number of functions documented within the header file > with those present as part of #define __BPF_FUNC_MAPPER and generates an > error in the header file and the man page if they don't match. It is not > needed with the currently documented upstream functions, but can help in > debugging when developing new helpers when there might be missing or > misformatted documentation. > > Signed-off-by: Usama Arif <usama.arif@xxxxxxxxxxxxx> > > --- > v4->v5: > - Converted warning to error incase of missing/misformatted helper doc > (suggested by Song Liu) I don't think it was converted to an error in the sense that Song meant it? Unless I'm missing something you simply changed the message so that it prints "error" instead of "warning", but the script still goes on without returning any error code, and a failure won't be detected by the CI for example. Could you make the script break out on errors, and print a message to stderr so that it's visible even if the generated output is redirected to a file, please? > > v3->v4: > - Added comments to make code clearer > - Added warning to man page as well (suggested by Quentin Monnet) > > v2->v3: > - Removed check if value is already in set (suggested by Song Liu) > > v1->v2: > - Fix CI error reported by Alexei Starovoitov > --- > scripts/bpf_doc.py | 74 +++++++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 70 insertions(+), 4 deletions(-) > > diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py > index a6403ddf5de7..adf08fa963a4 100755 > --- a/scripts/bpf_doc.py > +++ b/scripts/bpf_doc.py > @@ -87,6 +87,8 @@ class HeaderParser(object): > self.line = '' > self.helpers = [] > self.commands = [] > + self.desc_unique_helpers = set() > + self.define_unique_helpers = [] > > def parse_element(self): > proto = self.parse_symbol() > @@ -193,19 +195,42 @@ class HeaderParser(object): > except NoSyscallCommandFound: > break > > - def parse_helpers(self): > + def parse_desc_helpers(self): > self.seek_to('* Start of BPF helper function descriptions:', > 'Could not find start of eBPF helper descriptions list') > while True: > try: > helper = self.parse_helper() > self.helpers.append(helper) > + proto = helper.proto_break_down() > + self.desc_unique_helpers.add(proto['name']) > except NoHelperFound: > break > > + def parse_define_helpers(self): > + # Parse the number of FN(...) in #define __BPF_FUNC_MAPPER to compare > + # later with the number of unique function names present in description. > + # Note: seek_to(..) discards the first line below the target search text, > + # resulting in FN(unspec) being skipped and not added to self.define_unique_helpers. > + self.seek_to('#define __BPF_FUNC_MAPPER(FN)', > + 'Could not find start of eBPF helper definition list') > + # Searches for either one or more FN(\w+) defines or a backslash for newline > + p = re.compile('\s*(FN\(\w+\))+|\\\\') > + fn_defines_str = '' > + while True: > + capture = p.match(self.line) > + if capture: > + fn_defines_str += self.line > + else: > + break > + self.line = self.reader.readline() > + # Find the number of occurences of FN(\w+) > + self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str) > + > def run(self): > self.parse_syscall() > - self.parse_helpers() > + self.parse_desc_helpers() > + self.parse_define_helpers() > self.reader.close() > > ############################################################################### > @@ -305,9 +330,11 @@ class PrinterHelpersRST(PrinterRST): > """ > def __init__(self, parser): > self.elements = parser.helpers > + self.desc_unique_helpers = parser.desc_unique_helpers > + self.define_unique_helpers = parser.define_unique_helpers > > def print_header(self): > - header = '''\ > + header_name = '''\ > =========== > BPF-HELPERS > =========== > @@ -317,6 +344,8 @@ list of eBPF helper functions > > :Manual section: 7 > > +''' > + header_description = ''' > DESCRIPTION > =========== > > @@ -349,7 +378,27 @@ HELPERS > ======= > ''' > PrinterRST.print_license(self) > - print(header) > + > + print(header_name) > + > + # Add an error if the correct number of helpers are not auto-generated. > + nr_desc_unique_helpers = len(self.desc_unique_helpers) > + nr_define_unique_helpers = len(self.define_unique_helpers) > + if nr_desc_unique_helpers != nr_define_unique_helpers: > + header_error = ''' > +.. error:: > + The number of unique helpers in description (%d) don\'t match the number of unique helpers defined in __BPF_FUNC_MAPPER (%d) don\'t -> doesn\'t > +''' % (nr_desc_unique_helpers, nr_define_unique_helpers) > + if nr_desc_unique_helpers < nr_define_unique_helpers: > + # Function description is parsed until no helper is found (which can be due to > + # misformatting). Hence, only print the first missing/misformatted function. > + header_error += ''' > +.. error:: > + The description for %s is not present or formatted correctly. > +''' % (self.define_unique_helpers[nr_desc_unique_helpers]) > + print(header_error) > + > + print(header_description) > > def print_footer(self): > footer = ''' > @@ -509,6 +558,8 @@ class PrinterHelpers(Printer): > """ > def __init__(self, parser): > self.elements = parser.helpers > + self.desc_unique_helpers = parser.desc_unique_helpers > + self.define_unique_helpers = parser.define_unique_helpers > > type_fwds = [ > 'struct bpf_fib_lookup', > @@ -628,6 +679,21 @@ class PrinterHelpers(Printer): > /* Forward declarations of BPF structs */''' > > print(header) > + > + nr_desc_unique_helpers = len(self.desc_unique_helpers) > + nr_define_unique_helpers = len(self.define_unique_helpers) > + if nr_desc_unique_helpers != nr_define_unique_helpers: > + header_error = ''' > +#error The number of unique helpers in description (%d) don\'t match the number of unique helpers defined in __BPF_FUNC_MAPPER (%d) > +''' % (nr_desc_unique_helpers, nr_define_unique_helpers) > + if nr_desc_unique_helpers < nr_define_unique_helpers: > + # Function description is parsed until no helper is found (which can be due to > + # misformatting). Hence, only print the first missing/misformatted function. > + header_error += ''' > +#error The description for %s is not present or formatted correctly. > +''' % (self.define_unique_helpers[nr_desc_unique_helpers]) > + print(header_error) > + > for fwd in self.type_fwds: > print('%s;' % fwd) > print('')