History

scripts/bpf: Add syscall commands printer

Add a new target to bpf_doc.py to support generating the list of syscall commands directly from the UAPI headers. Assuming that developer submissions keep the main header up to date, this should allow the man pages to be automatically generated based on the latest API changes rather than requiring someone to separately go back through the API and describe each command.

Joe Stringer committed 5 years ago

Commit a67882a1 parent 923a9321 file · +91 −9Browse files

Showing the changes to scripts (1 of 1 file). Show all

Unified · Split

scripts/bpf_doc.py+91−9
@@ -14,6 +14,9 @@ import sys, os · 13 unmodified lines
1414 class NoHelperFound(BaseException):
1515 pass
1616
17+class NoSyscallCommandFound(BaseException):
18+ pass
19+
1720 class ParsingError(BaseException):
1821 def __init__(self, line='<line not provided>', reader=None):
1922 if reader:
@@ -23,18 +26,27 @@ class ParsingError(BaseException): · 3 unmodified lines
2326 else:
2427 BaseException.__init__(self, 'Error parsing line: %s' % line)
2528
26−class Helper(object):
29+
30+class APIElement(object):
2731 """
28− An object representing the description of an eBPF helper function.
29− @proto: function prototype of the helper function
30− @desc: textual description of the helper function
31− @ret: description of the return value of the helper function
32+ An object representing the description of an aspect of the eBPF API.
33+ @proto: prototype of the API symbol
34+ @desc: textual description of the symbol
35+ @ret: (optional) description of any associated return value
3236 """
3337 def __init__(self, proto='', desc='', ret=''):
3438 self.proto = proto
3539 self.desc = desc
3640 self.ret = ret
3741
42+
43+class Helper(APIElement):
44+ """
45+ An object representing the description of an eBPF helper function.
46+ @proto: function prototype of the helper function
47+ @desc: textual description of the helper function
48+ @ret: description of the return value of the helper function
49+ """
3850 def proto_break_down(self):
3951 """
4052 Break down helper function protocol into smaller chunks: return type,
@@ -61,6 +73,7 @@ class Helper(object): · 20 unmodified lines
6173
6274 return res
6375
76+
6477 class HeaderParser(object):
6578 """
6679 An object used to parse a file in order to extract the documentation of a
@@ -73,6 +86,13 @@ class HeaderParser(object): · 6 unmodified lines
7386 self.reader = open(filename, 'r')
7487 self.line = ''
7588 self.helpers = []
89+ self.commands = []
90+
91+ def parse_element(self):
92+ proto = self.parse_symbol()
93+ desc = self.parse_desc()
94+ ret = self.parse_ret()
95+ return APIElement(proto=proto, desc=desc, ret=ret)
7696
7797 def parse_helper(self):
7898 proto = self.parse_proto()
@@ -80,6 +100,18 @@ class HeaderParser(object): · 1 unmodified line
80100 ret = self.parse_ret()
81101 return Helper(proto=proto, desc=desc, ret=ret)
82102
103+ def parse_symbol(self):
104+ p = re.compile(' \* ?(.+)$')
105+ capture = p.match(self.line)
106+ if not capture:
107+ raise NoSyscallCommandFound
108+ end_re = re.compile(' \* ?NOTES$')
109+ end = end_re.match(self.line)
110+ if end:
111+ raise NoSyscallCommandFound
112+ self.line = self.reader.readline()
113+ return capture.group(1)
114+
83115 def parse_proto(self):
84116 # Argument can be of shape:
85117 # - "void"
@@ -141,16 +173,29 @@ class HeaderParser(object): · 55 unmodified lines
141173 break
142174 return ret
143175
144− def run(self):
145− # Advance to start of helper function descriptions.
146− offset = self.reader.read().find('* Start of BPF helper function descriptions:')
176+ def seek_to(self, target, help_message):
177+ self.reader.seek(0)
178+ offset = self.reader.read().find(target)
147179 if offset == -1:
148− raise Exception('Could not find start of eBPF helper descriptions list')
180+ raise Exception(help_message)
149181 self.reader.seek(offset)
150182 self.reader.readline()
151183 self.reader.readline()
152184 self.line = self.reader.readline()
153185
186+ def parse_syscall(self):
187+ self.seek_to('* DOC: eBPF Syscall Commands',
188+ 'Could not find start of eBPF syscall descriptions list')
189+ while True:
190+ try:
191+ command = self.parse_element()
192+ self.commands.append(command)
193+ except NoSyscallCommandFound:
194+ break
195+
196+ def parse_helpers(self):
197+ self.seek_to('* Start of BPF helper function descriptions:',
198+ 'Could not find start of eBPF helper descriptions list')
154199 while True:
155200 try:
156201 helper = self.parse_helper()
@@ -158,6 +203,9 @@ class HeaderParser(object): · 1 unmodified line
158203 except NoHelperFound:
159204 break
160205
206+ def run(self):
207+ self.parse_syscall()
208+ self.parse_helpers()
161209 self.reader.close()
162210
163211 ###############################################################################
@@ -420,6 +468,37 @@ SEE ALSO · 256 unmodified lines
420468 self.print_elem(helper)
421469
422470
471+class PrinterSyscallRST(PrinterRST):
472+ """
473+ A printer for dumping collected information about the syscall API as a
474+ ReStructured Text page compatible with the rst2man program, which can be
475+ used to generate a manual page for the syscall.
476+ @parser: A HeaderParser with APIElement objects to print to standard
477+ output
478+ """
479+ def __init__(self, parser):
480+ self.elements = parser.commands
481+
482+ def print_header(self):
483+ header = '''\
484+===
485+bpf
486+===
487+-------------------------------------------------------------------------------
488+Perform a command on an extended BPF object
489+-------------------------------------------------------------------------------
490+
491+:Manual section: 2
492+
493+COMMANDS
494+========
495+'''
496+ PrinterRST.print_license(self)
497+ print(header)
498+
499+ def print_one(self, command):
500+ print('**%s**' % (command.proto))
501+ self.print_elem(command)
423502
424503
425504 class PrinterHelpers(Printer):
@@ -620,6 +699,7 @@ bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') · 194 unmodified lines
620699
621700 printers = {
622701 'helpers': PrinterHelpersRST,
702+ 'syscall': PrinterSyscallRST,
623703 }
624704
625705 argParser = argparse.ArgumentParser(description="""
@@ -644,6 +724,8 @@ headerParser.run() · 18 unmodified lines
644724
645725 # Print formatted output to standard output.
646726 if args.header:
727+ if args.target != 'helpers':
728+ raise NotImplementedError('Only helpers header generation is supported')
647729 printer = PrinterHelpers(headerParser)
648730 else:
649731 printer = printers[args.target](headerParser)