#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (C) 2018-2019 Netronome Systems, Inc.
# Copyright (C) 2021 Isovalent, Inc.
# In case user attempts to run with Python 2.
from __future__ import print_function
import argparse
import json
import re
import sys, os
import subprocess
helpersDocStart = 'Start of BPF helper function descriptions:'
class NoHelperFound(BaseException):
pass
class NoSyscallCommandFound(BaseException):
pass
class ParsingError(BaseException):
def __init__(self, line='<line not provided>', reader=None):
if reader:
BaseException.__init__(self,
'Error at file offset %d, parsing line: %s' %
(reader.tell(), line))
else:
BaseException.__init__(self, 'Error parsing line: %s' % line)
class APIElement(object):
"""
An object representing the description of an aspect of the eBPF API.
@proto: prototype of the API symbol
@desc: textual description of the symbol
@ret: (optional) description of any associated return value
"""
def __init__(self, proto='', desc='', ret=''):
self.proto = proto
self.desc = desc
self.ret = ret
def to_dict(self):
return {
'proto': self.proto,
'desc': self.desc,
'ret': self.ret
}
class Helper(APIElement):
"""
An object representing the description of an eBPF helper function.
@proto: function prototype of the helper function
@desc: textual description of the helper function
@ret: description of the return value of the helper function
"""
def __init__(self, proto='', desc='', ret='', attrs=[]):
super().__init__(proto, desc, ret)
self.attrs = attrs
self.enum_val = None
def proto_break_down(self):
"""
Break down helper function protocol into smaller chunks: return type,
name, distincts arguments.
"""
arg_re = re.compile(r'((\w+ )*?(\w+|...))( (\**)(\w+))?$')
res = {}
proto_re = re.compile(r'(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$')
capture = proto_re.match(self.proto)
res['ret_type'] = capture.group(1)
res['ret_star'] = capture.group(2)
res['name'] = capture.group(3)
res['args'] = []
args = capture.group(4).split(', ')
for a in args:
capture = arg_re.match(a)
res['args'].append({
'type' : capture.group(1),
'star' : capture.group(5),
'name' : capture.group(6)
})
return res
def to_dict(self):
d = super().to_dict()
d["attrs"] = self.attrs
d.update(self.proto_break_down())
return d
ATTRS = {
'__bpf_fastcall': 'bpf_fastcall'
}
class HeaderParser(object):
"""
An object used to parse a file in order to extract the documentation of a
list of eBPF helper functions. All the helpers that can be retrieved are
stored as Helper object, in the self.helpers() array.
@filename: name of file to parse, usually include/uapi/linux/bpf.h in the
kernel tree
"""
def __init__(self, filename):
self.reader = open(filename, 'r')
self.line = ''
self.helpers = []
self.commands = []
self.desc_unique_helpers = set()
self.define_unique_helpers = []
self.helper_enum_vals = {}
self.helper_enum_pos = {}
self.desc_syscalls = []
self.enum_syscalls = []
def parse_element(self):
proto = self.parse_symbol()
desc = self.parse_desc(proto)
ret = self.parse_ret(proto)
return APIElement(proto=proto, desc=desc, ret=ret)
def parse_helper(self):
proto = self.parse_proto()
desc = self.parse_desc(proto)
ret = self.parse_ret(proto)
attrs = self.parse_attrs(proto)
return Helper(proto=proto, desc=desc, ret=ret, attrs=attrs)
def parse_symbol(self):
p = re.compile(r' \* ?(BPF\w+)$')
capture = p.match(self.line)
if not capture:
raise NoSyscallCommandFound
end_re = re.compile(r' \* ?NOTES$')
end = end_re.match(self.line)
if end:
raise NoSyscallCommandFound
self.line = self.reader.readline()
return capture.group(1)
def parse_proto(self):
# Argument can be of shape:
# - "void"
# - "type name"
# - "type *name"
# - Same as above, with "const" and/or "struct" in front of type
# - "..." (undefined number of arguments, for bpf_trace_printk())
# There is at least one term ("void"), and at most five arguments.
p = re.compile(r' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
capture = p.match(self.line)
if not capture:
raise NoHelperFound
self.line = self.reader.readline()
return capture.group(1)
def parse_desc(self,