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
89 self.commands = []89 self.commands = []
90 self.desc_unique_helpers = set()90 self.desc_unique_helpers = set()
91 self.define_unique_helpers = []91 self.define_unique_helpers = []
92 self.desc_syscalls = []
93 self.enum_syscalls = []
92 94
93 def parse_element(self):95 def parse_element(self):
94 proto = self.parse_symbol()96 proto = self.parse_symbol()
@@ -103,7 +105,7 @@ class HeaderParser(object): · 8 unmodified lines
103 return Helper(proto=proto, desc=desc, ret=ret)105 return Helper(proto=proto, desc=desc, ret=ret)
104 106
105 def parse_symbol(self):107 def parse_symbol(self):
106 p = re.compile(' \* ?(.+)$')108 p = re.compile(' \* ?(BPF\w+)$')
107 capture = p.match(self.line)109 capture = p.match(self.line)
108 if not capture:110 if not capture:
109 raise NoSyscallCommandFound111 raise NoSyscallCommandFound
@@ -181,26 +183,55 @@ class HeaderParser(object): · 71 unmodified lines
181 raise Exception("No return found for " + proto)183 raise Exception("No return found for " + proto)
182 return ret184 return ret
183 185
184 def seek_to(self, target, help_message):186 def seek_to(self, target, help_message, discard_lines = 1):
185 self.reader.seek(0)187 self.reader.seek(0)
186 offset = self.reader.read().find(target)188 offset = self.reader.read().find(target)
187 if offset == -1:189 if offset == -1:
188 raise Exception(help_message)190 raise Exception(help_message)
189 self.reader.seek(offset)191 self.reader.seek(offset)
190 self.reader.readline()192 self.reader.readline()
191 self.reader.readline()193 for _ in range(discard_lines):
194 self.reader.readline()
192 self.line = self.reader.readline()195 self.line = self.reader.readline()
193 196
194 def parse_syscall(self):197 def parse_desc_syscall(self):
195 self.seek_to('* DOC: eBPF Syscall Commands',198 self.seek_to('* DOC: eBPF Syscall Commands',
196 'Could not find start of eBPF syscall descriptions list')199 'Could not find start of eBPF syscall descriptions list')
197 while True:200 while True:
198 try:201 try:
199 command = self.parse_element()202 command = self.parse_element()
200 self.commands.append(command)203 self.commands.append(command)
204 self.desc_syscalls.append(command.proto)
205
201 except NoSyscallCommandFound:206 except NoSyscallCommandFound:
202 break207 break
203 208
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
204 def parse_desc_helpers(self):235 def parse_desc_helpers(self):
205 self.seek_to('* Start of BPF helper function descriptions:',236 self.seek_to('* Start of BPF helper function descriptions:',
206 'Could not find start of eBPF helper descriptions list')237 'Could not find start of eBPF helper descriptions list')
@@ -234,7 +265,8 @@ class HeaderParser(object): · 27 unmodified lines
234 self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str)265 self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str)
235 266
236 def run(self):267 def run(self):
237 self.parse_syscall()268 self.parse_desc_syscall()
269 self.parse_enum_syscall()
238 self.parse_desc_helpers()270 self.parse_desc_helpers()
239 self.parse_define_helpers()271 self.parse_define_helpers()
240 self.reader.close()272 self.reader.close()
@@ -266,6 +298,25 @@ class Printer(object): · 25 unmodified lines
266 self.print_one(elem)298 self.print_one(elem)
267 self.print_footer()299 self.print_footer()
268 300
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 = '''
311The 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 += '''
317The description for %s is not present or formatted correctly.
318''' % (define_unique_elem[nr_desc_unique_elem])
319 raise Exception(exception_msg)
269 320
270class PrinterRST(Printer):321class PrinterRST(Printer):
271 """322 """
@@ -326,26 +377,6 @@ class PrinterRST(Printer): · 54 unmodified lines
326 377
327 print('')378 print('')
328 379
329def 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 = '''
339The 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 += '''
345The description for %s is not present or formatted correctly.
346''' % (define_unique_helpers[nr_desc_unique_helpers])
347 raise Exception(helper_exception)
348
349class PrinterHelpersRST(PrinterRST):380class PrinterHelpersRST(PrinterRST):
350 """381 """
351 A printer for dumping collected information about helpers as a ReStructured382 A printer for dumping collected information about helpers as a ReStructured
@@ -355,7 +386,7 @@ class PrinterHelpersRST(PrinterRST): · 3 unmodified lines
355 """386 """
356 def __init__(self, parser):387 def __init__(self, parser):
357 self.elements = parser.helpers388 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')
359 390
360 def print_header(self):391 def print_header(self):
361 header = '''\392 header = '''\
@@ -529,6 +560,7 @@ class PrinterSyscallRST(PrinterRST): · 167 unmodified lines
529 """560 """
530 def __init__(self, parser):561 def __init__(self, parser):
531 self.elements = parser.commands562 self.elements = parser.commands
563 self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
532 564
533 def print_header(self):565 def print_header(self):
534 header = '''\566 header = '''\
@@ -560,7 +592,7 @@ class PrinterHelpers(Printer): · 25 unmodified lines
560 """592 """
561 def __init__(self, parser):593 def __init__(self, parser):
562 self.elements = parser.helpers594 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')
564 596
565 type_fwds = [597 type_fwds = [
566 'struct bpf_fib_lookup',598 'struct bpf_fib_lookup',