Commit 6f96674d authored by Quentin Monnet's avatar Quentin Monnet Committed by Daniel Borkmann

bpf: relax constraints on formatting for eBPF helper documentation

The Python script used to parse and extract eBPF helpers documentation
from include/uapi/linux/bpf.h expects a very specific formatting for the
descriptions (single dot represents a space, '>' stands for a tab):

    /*
     ...
     *.int bpf_helper(list of arguments)
     *.>    Description
     *.>    >       Start of description
     *.>    >       Another line of description
     *.>    >       And yet another line of description
     *.>    Return
     *.>    >       0 on success, or a negative error in case of failure
     ...
     */

This is too strict, and painful for developers who wants to add
documentation for new helpers. Worse, it is extremely difficult to check
that the formatting is correct during reviews. Change the format
expected by the script and make it more flexible. The script now works
whether or not the initial space (right after the star) is present, and
accepts both tabs and white spaces (or a combination of both) for
indenting description sections and contents.

Concretely, something like the following would now be supported:

    /*
     ...
     *int bpf_helper(list of arguments)
     *......Description
     *.>    >       Start of description...
     *>     >       Another line of description
     *..............And yet another line of description
     *>     Return
     *.>    ........0 on success, or a negative error in case of failure
     ...
     */

While at it, remove unnecessary carets from each regex used with match()
in the script. They are redundant, as match() tries to match from the
beginning of the string by default.

v2: Remove unnecessary caret when a regex is used with match().
Signed-off-by: default avatarQuentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent a2c7a983
...@@ -39,9 +39,9 @@ class Helper(object): ...@@ -39,9 +39,9 @@ class Helper(object):
Break down helper function protocol into smaller chunks: return type, Break down helper function protocol into smaller chunks: return type,
name, distincts arguments. name, distincts arguments.
""" """
arg_re = re.compile('^((const )?(struct )?(\w+|...))( (\**)(\w+))?$') arg_re = re.compile('((const )?(struct )?(\w+|...))( (\**)(\w+))?$')
res = {} res = {}
proto_re = re.compile('^(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') proto_re = re.compile('(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$')
capture = proto_re.match(self.proto) capture = proto_re.match(self.proto)
res['ret_type'] = capture.group(1) res['ret_type'] = capture.group(1)
...@@ -87,7 +87,7 @@ class HeaderParser(object): ...@@ -87,7 +87,7 @@ class HeaderParser(object):
# - Same as above, with "const" and/or "struct" in front of type # - Same as above, with "const" and/or "struct" in front of type
# - "..." (undefined number of arguments, for bpf_trace_printk()) # - "..." (undefined number of arguments, for bpf_trace_printk())
# There is at least one term ("void"), and at most five arguments. # There is at least one term ("void"), and at most five arguments.
p = re.compile('^ \* ((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') p = re.compile(' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
capture = p.match(self.line) capture = p.match(self.line)
if not capture: if not capture:
raise NoHelperFound raise NoHelperFound
...@@ -95,7 +95,7 @@ class HeaderParser(object): ...@@ -95,7 +95,7 @@ class HeaderParser(object):
return capture.group(1) return capture.group(1)
def parse_desc(self): def parse_desc(self):
p = re.compile('^ \* \tDescription$') p = re.compile(' \* ?(?:\t| {6,8})Description$')
capture = p.match(self.line) capture = p.match(self.line)
if not capture: if not capture:
# Helper can have empty description and we might be parsing another # Helper can have empty description and we might be parsing another
...@@ -109,7 +109,7 @@ class HeaderParser(object): ...@@ -109,7 +109,7 @@ class HeaderParser(object):
if self.line == ' *\n': if self.line == ' *\n':
desc += '\n' desc += '\n'
else: else:
p = re.compile('^ \* \t\t(.*)') p = re.compile(' \* ?(?:\t| {6,8})(?:\t| {8})(.*)')
capture = p.match(self.line) capture = p.match(self.line)
if capture: if capture:
desc += capture.group(1) + '\n' desc += capture.group(1) + '\n'
...@@ -118,7 +118,7 @@ class HeaderParser(object): ...@@ -118,7 +118,7 @@ class HeaderParser(object):
return desc return desc
def parse_ret(self): def parse_ret(self):
p = re.compile('^ \* \tReturn$') p = re.compile(' \* ?(?:\t| {6,8})Return$')
capture = p.match(self.line) capture = p.match(self.line)
if not capture: if not capture:
# Helper can have empty retval and we might be parsing another # Helper can have empty retval and we might be parsing another
...@@ -132,7 +132,7 @@ class HeaderParser(object): ...@@ -132,7 +132,7 @@ class HeaderParser(object):
if self.line == ' *\n': if self.line == ' *\n':
ret += '\n' ret += '\n'
else: else:
p = re.compile('^ \* \t\t(.*)') p = re.compile(' \* ?(?:\t| {6,8})(?:\t| {8})(.*)')
capture = p.match(self.line) capture = p.match(self.line)
if capture: if capture:
ret += capture.group(1) + '\n' ret += capture.group(1) + '\n'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment