Skip to content

Commit 0ba3929

Browse files
uarif1anakryiko
authored andcommitted
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. Signed-off-by: Usama Arif <usama.arif@bytedance.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Reviewed-by: Quentin Monnet <quentin@isovalent.com> Link: https://lore.kernel.org/bpf/20220119114442.1452088-3-usama.arif@bytedance.com
1 parent f1f3f67 commit 0ba3929

File tree

1 file changed

+59
-27
lines changed

1 file changed

+59
-27
lines changed

scripts/bpf_doc.py

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ def __init__(self, filename):
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 @@ def parse_helper(self):
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 @@ def parse_ret(self, proto):
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 @@ def parse_define_helpers(self):
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 @@ def print_all(self):
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 @@ def print_elem(self, elem):
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):
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):
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):
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',

0 commit comments

Comments
 (0)