History

bpf/scripts: Raise an exception if the correct number of sycalls are not generated

Currently the syscalls rst and subsequently man page are auto-generated using function documentation present in bpf.h. If the documentation for the syscall is missing or doesn't follow a specific format, then that syscall is not dumped in the auto-generated rst. This patch checks the number of syscalls documented within the header file with those present as part of the enum bpf_cmd and raises an Exception if they don't match. It is not needed with the currently documented upstream syscalls, but can help in debugging when developing new syscalls when there might be missing or misformatted documentation. The function helper_number_check is moved to the Printer parent class and renamed to elem_number_check as all the most derived children classes are using this function now.

Usama Arif committed 4 years ago

Commit 0ba39291 parent f1f3f671 file · +59 −27Browse files

Unified · Split

scripts/bpf_doc.py+59−27
@@ -89,6 +89,8 @@ class HeaderParser(object): · 88 unmodified lines
8989 self.commands = []
9090 self.desc_unique_helpers = set()
9191 self.define_unique_helpers = []
92+ self.desc_syscalls = []
93+ self.enum_syscalls = []
9294
9395 def parse_element(self):
9496 proto = self.parse_symbol()
@@ -103,7 +105,7 @@ class HeaderParser(object): · 8 unmodified lines
103105 return Helper(proto=proto, desc=desc, ret=ret)
104106
105107 def parse_symbol(self):
106− p = re.compile(' \* ?(.+)$')
108+ p = re.compile(' \* ?(BPF\w+)$')
107109 capture = p.match(self.line)
108110 if not capture:
109111 raise NoSyscallCommandFound
@@ -181,26 +183,55 @@ class HeaderParser(object): · 71 unmodified lines
181183 raise Exception("No return found for " + proto)
182184 return ret
183185
184− def seek_to(self, target, help_message):
186+ def seek_to(self, target, help_message, discard_lines = 1):
185187 self.reader.seek(0)
186188 offset = self.reader.read().find(target)
187189 if offset == -1:
188190 raise Exception(help_message)
189191 self.reader.seek(offset)
190192 self.reader.readline()
191− self.reader.readline()
193+ for _ in range(discard_lines):
194+ self.reader.readline()
192195 self.line = self.reader.readline()
193196
194− def parse_syscall(self):
197+ def parse_desc_syscall(self):
195198 self.seek_to('* DOC: eBPF Syscall Commands',
196199 'Could not find start of eBPF syscall descriptions list')
197200 while True:
198201 try:
199202 command = self.parse_element()
200203 self.commands.append(command)
204+ self.desc_syscalls.append(command.proto)
205+
201206 except NoSyscallCommandFound:
202207 break
203208
209+ def parse_enum_syscall(self):
210+ self.seek_to('enum bpf_cmd {',
211+ 'Could not find start of bpf_cmd enum', 0)
212+ # Searches for either one or more BPF\w+ enums
213+ bpf_p = re.compile('\s*(BPF\w+)+')
214+ # Searches for an enum entry assigned to another entry,
215+ # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is
216+ # not documented hence should be skipped in check to
217+ # determine if the right number of syscalls are documented
218+ assign_p = re.compile('\s*(BPF\w+)\s*=\s*(BPF\w+)')
219+ bpf_cmd_str = ''
220+ while True:
221+ capture = assign_p.match(self.line)
222+ if capture:
223+ # Skip line if an enum entry is assigned to another entry
224+ self.line = self.reader.readline()
225+ continue
226+ capture = bpf_p.match(self.line)
227+ if capture:
228+ bpf_cmd_str += self.line
229+ else:
230+ break
231+ self.line = self.reader.readline()
232+ # Find the number of occurences of BPF\w+
233+ self.enum_syscalls = re.findall('(BPF\w+)+', bpf_cmd_str)
234+
204235 def parse_desc_helpers(self):
205236 self.seek_to('* Start of BPF helper function descriptions:',
206237 'Could not find start of eBPF helper descriptions list')
@@ -234,7 +265,8 @@ class HeaderParser(object): · 27 unmodified lines
234265 self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str)
235266
236267 def run(self):
237− self.parse_syscall()
268+ self.parse_desc_syscall()
269+ self.parse_enum_syscall()
238270 self.parse_desc_helpers()
239271 self.parse_define_helpers()
240272 self.reader.close()
@@ -266,6 +298,25 @@ class Printer(object): · 25 unmodified lines
266298 self.print_one(elem)
267299 self.print_footer()
268300
301+ def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance):
302+ """
303+ Checks the number of helpers/syscalls documented within the header file
304+ description with those defined as part of enum/macro and raise an
305+ Exception if they don't match.
306+ """
307+ nr_desc_unique_elem = len(desc_unique_elem)
308+ nr_define_unique_elem = len(define_unique_elem)
309+ if nr_desc_unique_elem != nr_define_unique_elem:
310+ exception_msg = '''
311+The number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d)
312+''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem)
313+ if nr_desc_unique_elem < nr_define_unique_elem:
314+ # Function description is parsed until no helper is found (which can be due to
315+ # misformatting). Hence, only print the first missing/misformatted helper/enum.
316+ exception_msg += '''
317+The description for %s is not present or formatted correctly.
318+''' % (define_unique_elem[nr_desc_unique_elem])
319+ raise Exception(exception_msg)
269320
270321 class PrinterRST(Printer):
271322 """
@@ -326,26 +377,6 @@ class PrinterRST(Printer): · 54 unmodified lines
326377
327378 print('')
328379
329−def helper_number_check(desc_unique_helpers, define_unique_helpers):
330− """
331− Checks the number of functions documented within the header file
332− with those present as part of #define __BPF_FUNC_MAPPER and raise an
333− Exception if they don't match.
334− """
335− nr_desc_unique_helpers = len(desc_unique_helpers)
336− nr_define_unique_helpers = len(define_unique_helpers)
337− if nr_desc_unique_helpers != nr_define_unique_helpers:
338− helper_exception = '''
339−The number of unique helpers in description (%d) doesn\'t match the number of unique helpers defined in __BPF_FUNC_MAPPER (%d)
340−''' % (nr_desc_unique_helpers, nr_define_unique_helpers)
341− if nr_desc_unique_helpers < nr_define_unique_helpers:
342− # Function description is parsed until no helper is found (which can be due to
343− # misformatting). Hence, only print the first missing/misformatted function.
344− helper_exception += '''
345−The description for %s is not present or formatted correctly.
346−''' % (define_unique_helpers[nr_desc_unique_helpers])
347− raise Exception(helper_exception)
348−
349380 class PrinterHelpersRST(PrinterRST):
350381 """
351382 A printer for dumping collected information about helpers as a ReStructured
@@ -355,7 +386,7 @@ class PrinterHelpersRST(PrinterRST): · 3 unmodified lines
355386 """
356387 def __init__(self, parser):
357388 self.elements = parser.helpers
358− helper_number_check(parser.desc_unique_helpers, parser.define_unique_helpers)
389+ self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER')
359390
360391 def print_header(self):
361392 header = '''\
@@ -529,6 +560,7 @@ class PrinterSyscallRST(PrinterRST): · 167 unmodified lines
529560 """
530561 def __init__(self, parser):
531562 self.elements = parser.commands
563+ self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
532564
533565 def print_header(self):
534566 header = '''\
@@ -560,7 +592,7 @@ class PrinterHelpers(Printer): · 25 unmodified lines
560592 """
561593 def __init__(self, parser):
562594 self.elements = parser.helpers
563− helper_number_check(parser.desc_unique_helpers, parser.define_unique_helpers)
595+ self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER')
564596
565597 type_fwds = [
566598 'struct bpf_fib_lookup',