xref: /linux-6.15/scripts/bpf_doc.py (revision 92ec1cc3)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0-only
3#
4# Copyright (C) 2018-2019 Netronome Systems, Inc.
5# Copyright (C) 2021 Isovalent, Inc.
6
7# In case user attempts to run with Python 2.
8from __future__ import print_function
9
10import argparse
11import re
12import sys, os
13import subprocess
14
15helpersDocStart = 'Start of BPF helper function descriptions:'
16
17class NoHelperFound(BaseException):
18    pass
19
20class NoSyscallCommandFound(BaseException):
21    pass
22
23class ParsingError(BaseException):
24    def __init__(self, line='<line not provided>', reader=None):
25        if reader:
26            BaseException.__init__(self,
27                                   'Error at file offset %d, parsing line: %s' %
28                                   (reader.tell(), line))
29        else:
30            BaseException.__init__(self, 'Error parsing line: %s' % line)
31
32
33class APIElement(object):
34    """
35    An object representing the description of an aspect of the eBPF API.
36    @proto: prototype of the API symbol
37    @desc: textual description of the symbol
38    @ret: (optional) description of any associated return value
39    """
40    def __init__(self, proto='', desc='', ret=''):
41        self.proto = proto
42        self.desc = desc
43        self.ret = ret
44
45
46class Helper(APIElement):
47    """
48    An object representing the description of an eBPF helper function.
49    @proto: function prototype of the helper function
50    @desc: textual description of the helper function
51    @ret: description of the return value of the helper function
52    """
53    def proto_break_down(self):
54        """
55        Break down helper function protocol into smaller chunks: return type,
56        name, distincts arguments.
57        """
58        arg_re = re.compile('((\w+ )*?(\w+|...))( (\**)(\w+))?$')
59        res = {}
60        proto_re = re.compile('(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$')
61
62        capture = proto_re.match(self.proto)
63        res['ret_type'] = capture.group(1)
64        res['ret_star'] = capture.group(2)
65        res['name']     = capture.group(3)
66        res['args'] = []
67
68        args    = capture.group(4).split(', ')
69        for a in args:
70            capture = arg_re.match(a)
71            res['args'].append({
72                'type' : capture.group(1),
73                'star' : capture.group(5),
74                'name' : capture.group(6)
75            })
76
77        return res
78
79
80class HeaderParser(object):
81    """
82    An object used to parse a file in order to extract the documentation of a
83    list of eBPF helper functions. All the helpers that can be retrieved are
84    stored as Helper object, in the self.helpers() array.
85    @filename: name of file to parse, usually include/uapi/linux/bpf.h in the
86               kernel tree
87    """
88    def __init__(self, filename):
89        self.reader = open(filename, 'r')
90        self.line = ''
91        self.helpers = []
92        self.commands = []
93        self.desc_unique_helpers = set()
94        self.define_unique_helpers = []
95        self.desc_syscalls = []
96        self.enum_syscalls = []
97
98    def parse_element(self):
99        proto    = self.parse_symbol()
100        desc     = self.parse_desc(proto)
101        ret      = self.parse_ret(proto)
102        return APIElement(proto=proto, desc=desc, ret=ret)
103
104    def parse_helper(self):
105        proto    = self.parse_proto()
106        desc     = self.parse_desc(proto)
107        ret      = self.parse_ret(proto)
108        return Helper(proto=proto, desc=desc, ret=ret)
109
110    def parse_symbol(self):
111        p = re.compile(' \* ?(BPF\w+)$')
112        capture = p.match(self.line)
113        if not capture:
114            raise NoSyscallCommandFound
115        end_re = re.compile(' \* ?NOTES$')
116        end = end_re.match(self.line)
117        if end:
118            raise NoSyscallCommandFound
119        self.line = self.reader.readline()
120        return capture.group(1)
121
122    def parse_proto(self):
123        # Argument can be of shape:
124        #   - "void"
125        #   - "type  name"
126        #   - "type *name"
127        #   - Same as above, with "const" and/or "struct" in front of type
128        #   - "..." (undefined number of arguments, for bpf_trace_printk())
129        # There is at least one term ("void"), and at most five arguments.
130        p = re.compile(' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
131        capture = p.match(self.line)
132        if not capture:
133            raise NoHelperFound
134        self.line = self.reader.readline()
135        return capture.group(1)
136
137    def parse_desc(self, proto):
138        p = re.compile(' \* ?(?:\t| {5,8})Description$')
139        capture = p.match(self.line)
140        if not capture:
141            raise Exception("No description section found for " + proto)
142        # Description can be several lines, some of them possibly empty, and it
143        # stops when another subsection title is met.
144        desc = ''
145        desc_present = False
146        while True:
147            self.line = self.reader.readline()
148            if self.line == ' *\n':
149                desc += '\n'
150            else:
151                p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
152                capture = p.match(self.line)
153                if capture:
154                    desc_present = True
155                    desc += capture.group(1) + '\n'
156                else:
157                    break
158
159        if not desc_present:
160            raise Exception("No description found for " + proto)
161        return desc
162
163    def parse_ret(self, proto):
164        p = re.compile(' \* ?(?:\t| {5,8})Return$')
165        capture = p.match(self.line)
166        if not capture:
167            raise Exception("No return section found for " + proto)
168        # Return value description can be several lines, some of them possibly
169        # empty, and it stops when another subsection title is met.
170        ret = ''
171        ret_present = False
172        while True:
173            self.line = self.reader.readline()
174            if self.line == ' *\n':
175                ret += '\n'
176            else:
177                p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
178                capture = p.match(self.line)
179                if capture:
180                    ret_present = True
181                    ret += capture.group(1) + '\n'
182                else:
183                    break
184
185        if not ret_present:
186            raise Exception("No return found for " + proto)
187        return ret
188
189    def seek_to(self, target, help_message, discard_lines = 1):
190        self.reader.seek(0)
191        offset = self.reader.read().find(target)
192        if offset == -1:
193            raise Exception(help_message)
194        self.reader.seek(offset)
195        self.reader.readline()
196        for _ in range(discard_lines):
197            self.reader.readline()
198        self.line = self.reader.readline()
199
200    def parse_desc_syscall(self):
201        self.seek_to('* DOC: eBPF Syscall Commands',
202                     'Could not find start of eBPF syscall descriptions list')
203        while True:
204            try:
205                command = self.parse_element()
206                self.commands.append(command)
207                self.desc_syscalls.append(command.proto)
208
209            except NoSyscallCommandFound:
210                break
211
212    def parse_enum_syscall(self):
213        self.seek_to('enum bpf_cmd {',
214                     'Could not find start of bpf_cmd enum', 0)
215        # Searches for either one or more BPF\w+ enums
216        bpf_p = re.compile('\s*(BPF\w+)+')
217        # Searches for an enum entry assigned to another entry,
218        # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is
219        # not documented hence should be skipped in check to
220        # determine if the right number of syscalls are documented
221        assign_p = re.compile('\s*(BPF\w+)\s*=\s*(BPF\w+)')
222        bpf_cmd_str = ''
223        while True:
224            capture = assign_p.match(self.line)
225            if capture:
226                # Skip line if an enum entry is assigned to another entry
227                self.line = self.reader.readline()
228                continue
229            capture = bpf_p.match(self.line)
230            if capture:
231                bpf_cmd_str += self.line
232            else:
233                break
234            self.line = self.reader.readline()
235        # Find the number of occurences of BPF\w+
236        self.enum_syscalls = re.findall('(BPF\w+)+', bpf_cmd_str)
237
238    def parse_desc_helpers(self):
239        self.seek_to(helpersDocStart,
240                     'Could not find start of eBPF helper descriptions list')
241        while True:
242            try:
243                helper = self.parse_helper()
244                self.helpers.append(helper)
245                proto = helper.proto_break_down()
246                self.desc_unique_helpers.add(proto['name'])
247            except NoHelperFound:
248                break
249
250    def parse_define_helpers(self):
251        # Parse the number of FN(...) in #define __BPF_FUNC_MAPPER to compare
252        # later with the number of unique function names present in description.
253        # Note: seek_to(..) discards the first line below the target search text,
254        # resulting in FN(unspec) being skipped and not added to self.define_unique_helpers.
255        self.seek_to('#define __BPF_FUNC_MAPPER(FN)',
256                     'Could not find start of eBPF helper definition list')
257        # Searches for either one or more FN(\w+) defines or a backslash for newline
258        p = re.compile('\s*(FN\(\w+\))+|\\\\')
259        fn_defines_str = ''
260        while True:
261            capture = p.match(self.line)
262            if capture:
263                fn_defines_str += self.line
264            else:
265                break
266            self.line = self.reader.readline()
267        # Find the number of occurences of FN(\w+)
268        self.define_unique_helpers = re.findall('FN\(\w+\)', fn_defines_str)
269
270    def run(self):
271        self.parse_desc_syscall()
272        self.parse_enum_syscall()
273        self.parse_desc_helpers()
274        self.parse_define_helpers()
275        self.reader.close()
276
277###############################################################################
278
279class Printer(object):
280    """
281    A generic class for printers. Printers should be created with an array of
282    Helper objects, and implement a way to print them in the desired fashion.
283    @parser: A HeaderParser with objects to print to standard output
284    """
285    def __init__(self, parser):
286        self.parser = parser
287        self.elements = []
288
289    def print_header(self):
290        pass
291
292    def print_footer(self):
293        pass
294
295    def print_one(self, helper):
296        pass
297
298    def print_all(self):
299        self.print_header()
300        for elem in self.elements:
301            self.print_one(elem)
302        self.print_footer()
303
304    def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance):
305        """
306        Checks the number of helpers/syscalls documented within the header file
307        description with those defined as part of enum/macro and raise an
308        Exception if they don't match.
309        """
310        nr_desc_unique_elem = len(desc_unique_elem)
311        nr_define_unique_elem = len(define_unique_elem)
312        if nr_desc_unique_elem != nr_define_unique_elem:
313            exception_msg = '''
314The number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d)
315''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem)
316            if nr_desc_unique_elem < nr_define_unique_elem:
317                # Function description is parsed until no helper is found (which can be due to
318                # misformatting). Hence, only print the first missing/misformatted helper/enum.
319                exception_msg += '''
320The description for %s is not present or formatted correctly.
321''' % (define_unique_elem[nr_desc_unique_elem])
322            raise Exception(exception_msg)
323
324class PrinterRST(Printer):
325    """
326    A generic class for printers that print ReStructured Text. Printers should
327    be created with a HeaderParser object, and implement a way to print API
328    elements in the desired fashion.
329    @parser: A HeaderParser with objects to print to standard output
330    """
331    def __init__(self, parser):
332        self.parser = parser
333
334    def print_license(self):
335        license = '''\
336.. Copyright (C) All BPF authors and contributors from 2014 to present.
337.. See git log include/uapi/linux/bpf.h in kernel tree for details.
338..
339.. SPDX-License-Identifier:  Linux-man-pages-copyleft
340..
341.. Please do not edit this file. It was generated from the documentation
342.. located in file include/uapi/linux/bpf.h of the Linux kernel sources
343.. (helpers description), and from scripts/bpf_doc.py in the same
344.. repository (header and footer).
345'''
346        print(license)
347
348    def print_elem(self, elem):
349        if (elem.desc):
350            print('\tDescription')
351            # Do not strip all newline characters: formatted code at the end of
352            # a section must be followed by a blank line.
353            for line in re.sub('\n$', '', elem.desc, count=1).split('\n'):
354                print('{}{}'.format('\t\t' if line else '', line))
355
356        if (elem.ret):
357            print('\tReturn')
358            for line in elem.ret.rstrip().split('\n'):
359                print('{}{}'.format('\t\t' if line else '', line))
360
361        print('')
362
363    def get_kernel_version(self):
364        try:
365            version = subprocess.run(['git', 'describe'], cwd=linuxRoot,
366                                     capture_output=True, check=True)
367            version = version.stdout.decode().rstrip()
368        except:
369            try:
370                version = subprocess.run(['make', 'kernelversion'], cwd=linuxRoot,
371                                         capture_output=True, check=True)
372                version = version.stdout.decode().rstrip()
373            except:
374                return 'Linux'
375        return 'Linux {version}'.format(version=version)
376
377    def get_last_doc_update(self, delimiter):
378        try:
379            cmd = ['git', 'log', '-1', '--pretty=format:%cs', '--no-patch',
380                   '-L',
381                   '/{}/,/\*\//:include/uapi/linux/bpf.h'.format(delimiter)]
382            date = subprocess.run(cmd, cwd=linuxRoot,
383                                  capture_output=True, check=True)
384            return date.stdout.decode().rstrip()
385        except:
386            return ''
387
388class PrinterHelpersRST(PrinterRST):
389    """
390    A printer for dumping collected information about helpers as a ReStructured
391    Text page compatible with the rst2man program, which can be used to
392    generate a manual page for the helpers.
393    @parser: A HeaderParser with Helper objects to print to standard output
394    """
395    def __init__(self, parser):
396        self.elements = parser.helpers
397        self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER')
398
399    def print_header(self):
400        header = '''\
401===========
402BPF-HELPERS
403===========
404-------------------------------------------------------------------------------
405list of eBPF helper functions
406-------------------------------------------------------------------------------
407
408:Manual section: 7
409:Version: {version}
410{date_field}{date}
411
412DESCRIPTION
413===========
414
415The extended Berkeley Packet Filter (eBPF) subsystem consists in programs
416written in a pseudo-assembly language, then attached to one of the several
417kernel hooks and run in reaction of specific events. This framework differs
418from the older, "classic" BPF (or "cBPF") in several aspects, one of them being
419the ability to call special functions (or "helpers") from within a program.
420These functions are restricted to a white-list of helpers defined in the
421kernel.
422
423These helpers are used by eBPF programs to interact with the system, or with
424the context in which they work. For instance, they can be used to print
425debugging messages, to get the time since the system was booted, to interact
426with eBPF maps, or to manipulate network packets. Since there are several eBPF
427program types, and that they do not run in the same context, each program type
428can only call a subset of those helpers.
429
430Due to eBPF conventions, a helper can not have more than five arguments.
431
432Internally, eBPF programs call directly into the compiled helper functions
433without requiring any foreign-function interface. As a result, calling helpers
434introduces no overhead, thus offering excellent performance.
435
436This document is an attempt to list and document the helpers available to eBPF
437developers. They are sorted by chronological order (the oldest helpers in the
438kernel at the top).
439
440HELPERS
441=======
442'''
443        kernelVersion = self.get_kernel_version()
444        lastUpdate = self.get_last_doc_update(helpersDocStart)
445
446        PrinterRST.print_license(self)
447        print(header.format(version=kernelVersion,
448                            date_field = ':Date: ' if lastUpdate else '',
449                            date=lastUpdate))
450
451    def print_footer(self):
452        footer = '''
453EXAMPLES
454========
455
456Example usage for most of the eBPF helpers listed in this manual page are
457available within the Linux kernel sources, at the following locations:
458
459* *samples/bpf/*
460* *tools/testing/selftests/bpf/*
461
462LICENSE
463=======
464
465eBPF programs can have an associated license, passed along with the bytecode
466instructions to the kernel when the programs are loaded. The format for that
467string is identical to the one in use for kernel modules (Dual licenses, such
468as "Dual BSD/GPL", may be used). Some helper functions are only accessible to
469programs that are compatible with the GNU Privacy License (GPL).
470
471In order to use such helpers, the eBPF program must be loaded with the correct
472license string passed (via **attr**) to the **bpf**\ () system call, and this
473generally translates into the C source code of the program containing a line
474similar to the following:
475
476::
477
478	char ____license[] __attribute__((section("license"), used)) = "GPL";
479
480IMPLEMENTATION
481==============
482
483This manual page is an effort to document the existing eBPF helper functions.
484But as of this writing, the BPF sub-system is under heavy development. New eBPF
485program or map types are added, along with new helper functions. Some helpers
486are occasionally made available for additional program types. So in spite of
487the efforts of the community, this page might not be up-to-date. If you want to
488check by yourself what helper functions exist in your kernel, or what types of
489programs they can support, here are some files among the kernel tree that you
490may be interested in:
491
492* *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list
493  of all helper functions, as well as many other BPF definitions including most
494  of the flags, structs or constants used by the helpers.
495* *net/core/filter.c* contains the definition of most network-related helper
496  functions, and the list of program types from which they can be used.
497* *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related
498  helpers.
499* *kernel/bpf/verifier.c* contains the functions used to check that valid types
500  of eBPF maps are used with a given helper function.
501* *kernel/bpf/* directory contains other files in which additional helpers are
502  defined (for cgroups, sockmaps, etc.).
503* The bpftool utility can be used to probe the availability of helper functions
504  on the system (as well as supported program and map types, and a number of
505  other parameters). To do so, run **bpftool feature probe** (see
506  **bpftool-feature**\ (8) for details). Add the **unprivileged** keyword to
507  list features available to unprivileged users.
508
509Compatibility between helper functions and program types can generally be found
510in the files where helper functions are defined. Look for the **struct
511bpf_func_proto** objects and for functions returning them: these functions
512contain a list of helpers that a given program type can call. Note that the
513**default:** label of the **switch ... case** used to filter helpers can call
514other functions, themselves allowing access to additional helpers. The
515requirement for GPL license is also in those **struct bpf_func_proto**.
516
517Compatibility between helper functions and map types can be found in the
518**check_map_func_compatibility**\ () function in file *kernel/bpf/verifier.c*.
519
520Helper functions that invalidate the checks on **data** and **data_end**
521pointers for network processing are listed in function
522**bpf_helper_changes_pkt_data**\ () in file *net/core/filter.c*.
523
524SEE ALSO
525========
526
527**bpf**\ (2),
528**bpftool**\ (8),
529**cgroups**\ (7),
530**ip**\ (8),
531**perf_event_open**\ (2),
532**sendmsg**\ (2),
533**socket**\ (7),
534**tc-bpf**\ (8)'''
535        print(footer)
536
537    def print_proto(self, helper):
538        """
539        Format function protocol with bold and italics markers. This makes RST
540        file less readable, but gives nice results in the manual page.
541        """
542        proto = helper.proto_break_down()
543
544        print('**%s %s%s(' % (proto['ret_type'],
545                              proto['ret_star'].replace('*', '\\*'),
546                              proto['name']),
547              end='')
548
549        comma = ''
550        for a in proto['args']:
551            one_arg = '{}{}'.format(comma, a['type'])
552            if a['name']:
553                if a['star']:
554                    one_arg += ' {}**\ '.format(a['star'].replace('*', '\\*'))
555                else:
556                    one_arg += '** '
557                one_arg += '*{}*\\ **'.format(a['name'])
558            comma = ', '
559            print(one_arg, end='')
560
561        print(')**')
562
563    def print_one(self, helper):
564        self.print_proto(helper)
565        self.print_elem(helper)
566
567
568class PrinterSyscallRST(PrinterRST):
569    """
570    A printer for dumping collected information about the syscall API as a
571    ReStructured Text page compatible with the rst2man program, which can be
572    used to generate a manual page for the syscall.
573    @parser: A HeaderParser with APIElement objects to print to standard
574             output
575    """
576    def __init__(self, parser):
577        self.elements = parser.commands
578        self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
579
580    def print_header(self):
581        header = '''\
582===
583bpf
584===
585-------------------------------------------------------------------------------
586Perform a command on an extended BPF object
587-------------------------------------------------------------------------------
588
589:Manual section: 2
590
591COMMANDS
592========
593'''
594        PrinterRST.print_license(self)
595        print(header)
596
597    def print_one(self, command):
598        print('**%s**' % (command.proto))
599        self.print_elem(command)
600
601
602class PrinterHelpers(Printer):
603    """
604    A printer for dumping collected information about helpers as C header to
605    be included from BPF program.
606    @parser: A HeaderParser with Helper objects to print to standard output
607    """
608    def __init__(self, parser):
609        self.elements = parser.helpers
610        self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '__BPF_FUNC_MAPPER')
611
612    type_fwds = [
613            'struct bpf_fib_lookup',
614            'struct bpf_sk_lookup',
615            'struct bpf_perf_event_data',
616            'struct bpf_perf_event_value',
617            'struct bpf_pidns_info',
618            'struct bpf_redir_neigh',
619            'struct bpf_sock',
620            'struct bpf_sock_addr',
621            'struct bpf_sock_ops',
622            'struct bpf_sock_tuple',
623            'struct bpf_spin_lock',
624            'struct bpf_sysctl',
625            'struct bpf_tcp_sock',
626            'struct bpf_tunnel_key',
627            'struct bpf_xfrm_state',
628            'struct linux_binprm',
629            'struct pt_regs',
630            'struct sk_reuseport_md',
631            'struct sockaddr',
632            'struct tcphdr',
633            'struct seq_file',
634            'struct tcp6_sock',
635            'struct tcp_sock',
636            'struct tcp_timewait_sock',
637            'struct tcp_request_sock',
638            'struct udp6_sock',
639            'struct unix_sock',
640            'struct task_struct',
641
642            'struct __sk_buff',
643            'struct sk_msg_md',
644            'struct xdp_md',
645            'struct path',
646            'struct btf_ptr',
647            'struct inode',
648            'struct socket',
649            'struct file',
650            'struct bpf_timer',
651            'struct mptcp_sock',
652            'struct bpf_dynptr',
653            'struct iphdr',
654            'struct ipv6hdr',
655    ]
656    known_types = {
657            '...',
658            'void',
659            'const void',
660            'char',
661            'const char',
662            'int',
663            'long',
664            'unsigned long',
665
666            '__be16',
667            '__be32',
668            '__wsum',
669
670            'struct bpf_fib_lookup',
671            'struct bpf_perf_event_data',
672            'struct bpf_perf_event_value',
673            'struct bpf_pidns_info',
674            'struct bpf_redir_neigh',
675            'struct bpf_sk_lookup',
676            'struct bpf_sock',
677            'struct bpf_sock_addr',
678            'struct bpf_sock_ops',
679            'struct bpf_sock_tuple',
680            'struct bpf_spin_lock',
681            'struct bpf_sysctl',
682            'struct bpf_tcp_sock',
683            'struct bpf_tunnel_key',
684            'struct bpf_xfrm_state',
685            'struct linux_binprm',
686            'struct pt_regs',
687            'struct sk_reuseport_md',
688            'struct sockaddr',
689            'struct tcphdr',
690            'struct seq_file',
691            'struct tcp6_sock',
692            'struct tcp_sock',
693            'struct tcp_timewait_sock',
694            'struct tcp_request_sock',
695            'struct udp6_sock',
696            'struct unix_sock',
697            'struct task_struct',
698            'struct path',
699            'struct btf_ptr',
700            'struct inode',
701            'struct socket',
702            'struct file',
703            'struct bpf_timer',
704            'struct mptcp_sock',
705            'struct bpf_dynptr',
706            'struct iphdr',
707            'struct ipv6hdr',
708    }
709    mapped_types = {
710            'u8': '__u8',
711            'u16': '__u16',
712            'u32': '__u32',
713            'u64': '__u64',
714            's8': '__s8',
715            's16': '__s16',
716            's32': '__s32',
717            's64': '__s64',
718            'size_t': 'unsigned long',
719            'struct bpf_map': 'void',
720            'struct sk_buff': 'struct __sk_buff',
721            'const struct sk_buff': 'const struct __sk_buff',
722            'struct sk_msg_buff': 'struct sk_msg_md',
723            'struct xdp_buff': 'struct xdp_md',
724    }
725    # Helpers overloaded for different context types.
726    overloaded_helpers = [
727        'bpf_get_socket_cookie',
728        'bpf_sk_assign',
729    ]
730
731    def print_header(self):
732        header = '''\
733/* This is auto-generated file. See bpf_doc.py for details. */
734
735/* Forward declarations of BPF structs */'''
736
737        print(header)
738        for fwd in self.type_fwds:
739            print('%s;' % fwd)
740        print('')
741
742    def print_footer(self):
743        footer = ''
744        print(footer)
745
746    def map_type(self, t):
747        if t in self.known_types:
748            return t
749        if t in self.mapped_types:
750            return self.mapped_types[t]
751        print("Unrecognized type '%s', please add it to known types!" % t,
752              file=sys.stderr)
753        sys.exit(1)
754
755    seen_helpers = set()
756
757    def print_one(self, helper):
758        proto = helper.proto_break_down()
759
760        if proto['name'] in self.seen_helpers:
761            return
762        self.seen_helpers.add(proto['name'])
763
764        print('/*')
765        print(" * %s" % proto['name'])
766        print(" *")
767        if (helper.desc):
768            # Do not strip all newline characters: formatted code at the end of
769            # a section must be followed by a blank line.
770            for line in re.sub('\n$', '', helper.desc, count=1).split('\n'):
771                print(' *{}{}'.format(' \t' if line else '', line))
772
773        if (helper.ret):
774            print(' *')
775            print(' * Returns')
776            for line in helper.ret.rstrip().split('\n'):
777                print(' *{}{}'.format(' \t' if line else '', line))
778
779        print(' */')
780        print('static %s %s(*%s)(' % (self.map_type(proto['ret_type']),
781                                      proto['ret_star'], proto['name']), end='')
782        comma = ''
783        for i, a in enumerate(proto['args']):
784            t = a['type']
785            n = a['name']
786            if proto['name'] in self.overloaded_helpers and i == 0:
787                    t = 'void'
788                    n = 'ctx'
789            one_arg = '{}{}'.format(comma, self.map_type(t))
790            if n:
791                if a['star']:
792                    one_arg += ' {}'.format(a['star'])
793                else:
794                    one_arg += ' '
795                one_arg += '{}'.format(n)
796            comma = ', '
797            print(one_arg, end='')
798
799        print(') = (void *) %d;' % len(self.seen_helpers))
800        print('')
801
802###############################################################################
803
804# If script is launched from scripts/ from kernel tree and can access
805# ../include/uapi/linux/bpf.h, use it as a default name for the file to parse,
806# otherwise the --filename argument will be required from the command line.
807script = os.path.abspath(sys.argv[0])
808linuxRoot = os.path.dirname(os.path.dirname(script))
809bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h')
810
811printers = {
812        'helpers': PrinterHelpersRST,
813        'syscall': PrinterSyscallRST,
814}
815
816argParser = argparse.ArgumentParser(description="""
817Parse eBPF header file and generate documentation for the eBPF API.
818The RST-formatted output produced can be turned into a manual page with the
819rst2man utility.
820""")
821argParser.add_argument('--header', action='store_true',
822                       help='generate C header file')
823if (os.path.isfile(bpfh)):
824    argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h',
825                           default=bpfh)
826else:
827    argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h')
828argParser.add_argument('target', nargs='?', default='helpers',
829                       choices=printers.keys(), help='eBPF API target')
830args = argParser.parse_args()
831
832# Parse file.
833headerParser = HeaderParser(args.filename)
834headerParser.run()
835
836# Print formatted output to standard output.
837if args.header:
838    if args.target != 'helpers':
839        raise NotImplementedError('Only helpers header generation is supported')
840    printer = PrinterHelpers(headerParser)
841else:
842    printer = printers[args.target](headerParser)
843printer.print_all()
844