mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-15 23:25:07 +00:00
9bc95a95ab
Now 'BPF_MAP_TYPE_CGRP_STORAGE + local percpu ptr' can cover all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE functionality and more. So mark BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE deprecated. Also make changes in selftests/bpf/test_bpftool_synctypes.py and selftest libbpf_str to fix otherwise test errors. Signed-off-by: Yonghong Song <yonghong.song@linux.dev> Link: https://lore.kernel.org/r/20230827152837.2003563-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
627 lines
22 KiB
Python
Executable file
627 lines
22 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
#
|
|
# Copyright (C) 2021 Isovalent, Inc.
|
|
|
|
import argparse
|
|
import re
|
|
import os, sys
|
|
|
|
LINUX_ROOT = os.path.abspath(os.path.join(__file__,
|
|
os.pardir, os.pardir, os.pardir, os.pardir, os.pardir))
|
|
BPFTOOL_DIR = os.getenv('BPFTOOL_DIR',
|
|
os.path.join(LINUX_ROOT, 'tools/bpf/bpftool'))
|
|
BPFTOOL_BASHCOMP_DIR = os.getenv('BPFTOOL_BASHCOMP_DIR',
|
|
os.path.join(BPFTOOL_DIR, 'bash-completion'))
|
|
BPFTOOL_DOC_DIR = os.getenv('BPFTOOL_DOC_DIR',
|
|
os.path.join(BPFTOOL_DIR, 'Documentation'))
|
|
INCLUDE_DIR = os.getenv('INCLUDE_DIR',
|
|
os.path.join(LINUX_ROOT, 'tools/include'))
|
|
|
|
retval = 0
|
|
|
|
class BlockParser(object):
|
|
"""
|
|
A parser for extracting set of values from blocks such as enums.
|
|
@reader: a pointer to the open file to parse
|
|
"""
|
|
def __init__(self, reader):
|
|
self.reader = reader
|
|
|
|
def search_block(self, start_marker):
|
|
"""
|
|
Search for a given structure in a file.
|
|
@start_marker: regex marking the beginning of a structure to parse
|
|
"""
|
|
offset = self.reader.tell()
|
|
array_start = re.search(start_marker, self.reader.read())
|
|
if array_start is None:
|
|
raise Exception('Failed to find start of block')
|
|
self.reader.seek(offset + array_start.start())
|
|
|
|
def parse(self, pattern, end_marker):
|
|
"""
|
|
Parse a block and return a set of values. Values to extract must be
|
|
on separate lines in the file.
|
|
@pattern: pattern used to identify the values to extract
|
|
@end_marker: regex marking the end of the block to parse
|
|
"""
|
|
entries = set()
|
|
while True:
|
|
line = self.reader.readline()
|
|
if not line or re.match(end_marker, line):
|
|
break
|
|
capture = pattern.search(line)
|
|
if capture and pattern.groups >= 1:
|
|
entries.add(capture.group(1))
|
|
return entries
|
|
|
|
class ArrayParser(BlockParser):
|
|
"""
|
|
A parser for extracting a set of values from some BPF-related arrays.
|
|
@reader: a pointer to the open file to parse
|
|
@array_name: name of the array to parse
|
|
"""
|
|
end_marker = re.compile('^};')
|
|
|
|
def __init__(self, reader, array_name):
|
|
self.array_name = array_name
|
|
self.start_marker = re.compile(f'(static )?const bool {self.array_name}\[.*\] = {{\n')
|
|
super().__init__(reader)
|
|
|
|
def search_block(self):
|
|
"""
|
|
Search for the given array in a file.
|
|
"""
|
|
super().search_block(self.start_marker);
|
|
|
|
def parse(self):
|
|
"""
|
|
Parse a block and return data as a dictionary. Items to extract must be
|
|
on separate lines in the file.
|
|
"""
|
|
pattern = re.compile('\[(BPF_\w*)\]\s*= (true|false),?$')
|
|
entries = set()
|
|
while True:
|
|
line = self.reader.readline()
|
|
if line == '' or re.match(self.end_marker, line):
|
|
break
|
|
capture = pattern.search(line)
|
|
if capture:
|
|
entries |= {capture.group(1)}
|
|
return entries
|
|
|
|
class InlineListParser(BlockParser):
|
|
"""
|
|
A parser for extracting set of values from inline lists.
|
|
"""
|
|
def parse(self, pattern, end_marker):
|
|
"""
|
|
Parse a block and return a set of values. Multiple values to extract
|
|
can be on a same line in the file.
|
|
@pattern: pattern used to identify the values to extract
|
|
@end_marker: regex marking the end of the block to parse
|
|
"""
|
|
entries = set()
|
|
while True:
|
|
line = self.reader.readline()
|
|
if not line:
|
|
break
|
|
entries.update(pattern.findall(line))
|
|
if re.search(end_marker, line):
|
|
break
|
|
return entries
|
|
|
|
class FileExtractor(object):
|
|
"""
|
|
A generic reader for extracting data from a given file. This class contains
|
|
several helper methods that wrap around parser objects to extract values
|
|
from different structures.
|
|
This class does not offer a way to set a filename, which is expected to be
|
|
defined in children classes.
|
|
"""
|
|
def __init__(self):
|
|
self.reader = open(self.filename, 'r')
|
|
|
|
def close(self):
|
|
"""
|
|
Close the file used by the parser.
|
|
"""
|
|
self.reader.close()
|
|
|
|
def reset_read(self):
|
|
"""
|
|
Reset the file position indicator for this parser. This is useful when
|
|
parsing several structures in the file without respecting the order in
|
|
which those structures appear in the file.
|
|
"""
|
|
self.reader.seek(0)
|
|
|
|
def get_types_from_array(self, array_name):
|
|
"""
|
|
Search for and parse a list of allowed BPF_* enum members, for example:
|
|
|
|
const bool prog_type_name[] = {
|
|
[BPF_PROG_TYPE_UNSPEC] = true,
|
|
[BPF_PROG_TYPE_SOCKET_FILTER] = true,
|
|
[BPF_PROG_TYPE_KPROBE] = true,
|
|
};
|
|
|
|
Return a set of the enum members, for example:
|
|
|
|
{'BPF_PROG_TYPE_UNSPEC',
|
|
'BPF_PROG_TYPE_SOCKET_FILTER',
|
|
'BPF_PROG_TYPE_KPROBE'}
|
|
|
|
@array_name: name of the array to parse
|
|
"""
|
|
array_parser = ArrayParser(self.reader, array_name)
|
|
array_parser.search_block()
|
|
return array_parser.parse()
|
|
|
|
def get_enum(self, enum_name):
|
|
"""
|
|
Search for and parse an enum containing BPF_* members, for example:
|
|
|
|
enum bpf_prog_type {
|
|
BPF_PROG_TYPE_UNSPEC,
|
|
BPF_PROG_TYPE_SOCKET_FILTER,
|
|
BPF_PROG_TYPE_KPROBE,
|
|
};
|
|
|
|
Return a set containing all member names, for example:
|
|
|
|
{'BPF_PROG_TYPE_UNSPEC',
|
|
'BPF_PROG_TYPE_SOCKET_FILTER',
|
|
'BPF_PROG_TYPE_KPROBE'}
|
|
|
|
@enum_name: name of the enum to parse
|
|
"""
|
|
start_marker = re.compile(f'enum {enum_name} {{\n')
|
|
pattern = re.compile('^\s*(BPF_\w+),?(\s+/\*.*\*/)?$')
|
|
end_marker = re.compile('^};')
|
|
parser = BlockParser(self.reader)
|
|
parser.search_block(start_marker)
|
|
return parser.parse(pattern, end_marker)
|
|
|
|
def make_enum_map(self, names, enum_prefix):
|
|
"""
|
|
Search for and parse an enum containing BPF_* members, just as get_enum
|
|
does. However, instead of just returning a set of the variant names,
|
|
also generate a textual representation from them by (assuming and)
|
|
removing a provided prefix and lowercasing the remainder. Then return a
|
|
dict mapping from name to textual representation.
|
|
|
|
@enum_values: a set of enum values; e.g., as retrieved by get_enum
|
|
@enum_prefix: the prefix to remove from each of the variants to infer
|
|
textual representation
|
|
"""
|
|
mapping = {}
|
|
for name in names:
|
|
if not name.startswith(enum_prefix):
|
|
raise Exception(f"enum variant {name} does not start with {enum_prefix}")
|
|
text = name[len(enum_prefix):].lower()
|
|
mapping[name] = text
|
|
|
|
return mapping
|
|
|
|
def __get_description_list(self, start_marker, pattern, end_marker):
|
|
parser = InlineListParser(self.reader)
|
|
parser.search_block(start_marker)
|
|
return parser.parse(pattern, end_marker)
|
|
|
|
def get_rst_list(self, block_name):
|
|
"""
|
|
Search for and parse a list of type names from RST documentation, for
|
|
example:
|
|
|
|
| *TYPE* := {
|
|
| **socket** | **kprobe** |
|
|
| **kretprobe**
|
|
| }
|
|
|
|
Return a set containing all type names, for example:
|
|
|
|
{'socket', 'kprobe', 'kretprobe'}
|
|
|
|
@block_name: name of the blog to parse, 'TYPE' in the example
|
|
"""
|
|
start_marker = re.compile(f'\*{block_name}\* := {{')
|
|
pattern = re.compile('\*\*([\w/-]+)\*\*')
|
|
end_marker = re.compile('}\n')
|
|
return self.__get_description_list(start_marker, pattern, end_marker)
|
|
|
|
def get_help_list(self, block_name):
|
|
"""
|
|
Search for and parse a list of type names from a help message in
|
|
bpftool, for example:
|
|
|
|
" TYPE := { socket | kprobe |\\n"
|
|
" kretprobe }\\n"
|
|
|
|
Return a set containing all type names, for example:
|
|
|
|
{'socket', 'kprobe', 'kretprobe'}
|
|
|
|
@block_name: name of the blog to parse, 'TYPE' in the example
|
|
"""
|
|
start_marker = re.compile(f'"\s*{block_name} := {{')
|
|
pattern = re.compile('([\w/]+) [|}]')
|
|
end_marker = re.compile('}')
|
|
return self.__get_description_list(start_marker, pattern, end_marker)
|
|
|
|
def get_help_list_macro(self, macro):
|
|
"""
|
|
Search for and parse a list of values from a help message starting with
|
|
a macro in bpftool, for example:
|
|
|
|
" " HELP_SPEC_OPTIONS " |\\n"
|
|
" {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} }\\n"
|
|
|
|
Return a set containing all item names, for example:
|
|
|
|
{'-f', '--bpffs', '-m', '--mapcompat', '-n', '--nomount'}
|
|
|
|
@macro: macro starting the block, 'HELP_SPEC_OPTIONS' in the example
|
|
"""
|
|
start_marker = re.compile(f'"\s*{macro}\s*" [|}}]')
|
|
pattern = re.compile('([\w-]+) ?(?:\||}[ }\]])')
|
|
end_marker = re.compile('}\\\\n')
|
|
return self.__get_description_list(start_marker, pattern, end_marker)
|
|
|
|
def get_bashcomp_list(self, block_name):
|
|
"""
|
|
Search for and parse a list of type names from a variable in bash
|
|
completion file, for example:
|
|
|
|
local BPFTOOL_PROG_LOAD_TYPES='socket kprobe \\
|
|
kretprobe'
|
|
|
|
Return a set containing all type names, for example:
|
|
|
|
{'socket', 'kprobe', 'kretprobe'}
|
|
|
|
@block_name: name of the blog to parse, 'TYPE' in the example
|
|
"""
|
|
start_marker = re.compile(f'local {block_name}=\'')
|
|
pattern = re.compile('(?:.*=\')?([\w/]+)')
|
|
end_marker = re.compile('\'$')
|
|
return self.__get_description_list(start_marker, pattern, end_marker)
|
|
|
|
class SourceFileExtractor(FileExtractor):
|
|
"""
|
|
An abstract extractor for a source file with usage message.
|
|
This class does not offer a way to set a filename, which is expected to be
|
|
defined in children classes.
|
|
"""
|
|
def get_options(self):
|
|
return self.get_help_list_macro('HELP_SPEC_OPTIONS')
|
|
|
|
class MainHeaderFileExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for bpftool's main.h
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DIR, 'main.h')
|
|
|
|
def get_common_options(self):
|
|
"""
|
|
Parse the list of common options in main.h (options that apply to all
|
|
commands), which looks to the lists of options in other source files
|
|
but has different start and end markers:
|
|
|
|
"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug}"
|
|
|
|
Return a set containing all options, such as:
|
|
|
|
{'-p', '-d', '--pretty', '--debug', '--json', '-j'}
|
|
"""
|
|
start_marker = re.compile(f'"OPTIONS :=')
|
|
pattern = re.compile('([\w-]+) ?(?:\||}[ }\]"])')
|
|
end_marker = re.compile('#define')
|
|
|
|
parser = InlineListParser(self.reader)
|
|
parser.search_block(start_marker)
|
|
return parser.parse(pattern, end_marker)
|
|
|
|
class ManSubstitutionsExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for substitutions.rst
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DOC_DIR, 'substitutions.rst')
|
|
|
|
def get_common_options(self):
|
|
"""
|
|
Parse the list of common options in substitutions.rst (options that
|
|
apply to all commands).
|
|
|
|
Return a set containing all options, such as:
|
|
|
|
{'-p', '-d', '--pretty', '--debug', '--json', '-j'}
|
|
"""
|
|
start_marker = re.compile('\|COMMON_OPTIONS\| replace:: {')
|
|
pattern = re.compile('\*\*([\w/-]+)\*\*')
|
|
end_marker = re.compile('}$')
|
|
|
|
parser = InlineListParser(self.reader)
|
|
parser.search_block(start_marker)
|
|
return parser.parse(pattern, end_marker)
|
|
|
|
class ProgFileExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for bpftool's prog.c.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DIR, 'prog.c')
|
|
|
|
def get_attach_types(self):
|
|
types = self.get_types_from_array('attach_types')
|
|
return self.make_enum_map(types, 'BPF_')
|
|
|
|
def get_prog_attach_help(self):
|
|
return self.get_help_list('ATTACH_TYPE')
|
|
|
|
class MapFileExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for bpftool's map.c.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DIR, 'map.c')
|
|
|
|
def get_map_help(self):
|
|
return self.get_help_list('TYPE')
|
|
|
|
class CgroupFileExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for bpftool's cgroup.c.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DIR, 'cgroup.c')
|
|
|
|
def get_prog_attach_help(self):
|
|
return self.get_help_list('ATTACH_TYPE')
|
|
|
|
class GenericSourceExtractor(SourceFileExtractor):
|
|
"""
|
|
An extractor for generic source code files.
|
|
"""
|
|
filename = ""
|
|
|
|
def __init__(self, filename):
|
|
self.filename = os.path.join(BPFTOOL_DIR, filename)
|
|
super().__init__()
|
|
|
|
class BpfHeaderExtractor(FileExtractor):
|
|
"""
|
|
An extractor for the UAPI BPF header.
|
|
"""
|
|
filename = os.path.join(INCLUDE_DIR, 'uapi/linux/bpf.h')
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.attach_types = {}
|
|
|
|
def get_prog_types(self):
|
|
return self.get_enum('bpf_prog_type')
|
|
|
|
def get_map_type_map(self):
|
|
names = self.get_enum('bpf_map_type')
|
|
return self.make_enum_map(names, 'BPF_MAP_TYPE_')
|
|
|
|
def get_attach_type_map(self):
|
|
if not self.attach_types:
|
|
names = self.get_enum('bpf_attach_type')
|
|
self.attach_types = self.make_enum_map(names, 'BPF_')
|
|
return self.attach_types
|
|
|
|
def get_cgroup_attach_type_map(self):
|
|
if not self.attach_types:
|
|
self.get_attach_type_map()
|
|
return {name: text for name, text in self.attach_types.items()
|
|
if name.startswith('BPF_CGROUP')}
|
|
|
|
class ManPageExtractor(FileExtractor):
|
|
"""
|
|
An abstract extractor for an RST documentation page.
|
|
This class does not offer a way to set a filename, which is expected to be
|
|
defined in children classes.
|
|
"""
|
|
def get_options(self):
|
|
return self.get_rst_list('OPTIONS')
|
|
|
|
class ManProgExtractor(ManPageExtractor):
|
|
"""
|
|
An extractor for bpftool-prog.rst.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DOC_DIR, 'bpftool-prog.rst')
|
|
|
|
def get_attach_types(self):
|
|
return self.get_rst_list('ATTACH_TYPE')
|
|
|
|
class ManMapExtractor(ManPageExtractor):
|
|
"""
|
|
An extractor for bpftool-map.rst.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DOC_DIR, 'bpftool-map.rst')
|
|
|
|
def get_map_types(self):
|
|
return self.get_rst_list('TYPE')
|
|
|
|
class ManCgroupExtractor(ManPageExtractor):
|
|
"""
|
|
An extractor for bpftool-cgroup.rst.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_DOC_DIR, 'bpftool-cgroup.rst')
|
|
|
|
def get_attach_types(self):
|
|
return self.get_rst_list('ATTACH_TYPE')
|
|
|
|
class ManGenericExtractor(ManPageExtractor):
|
|
"""
|
|
An extractor for generic RST documentation pages.
|
|
"""
|
|
filename = ""
|
|
|
|
def __init__(self, filename):
|
|
self.filename = os.path.join(BPFTOOL_DIR, filename)
|
|
super().__init__()
|
|
|
|
class BashcompExtractor(FileExtractor):
|
|
"""
|
|
An extractor for bpftool's bash completion file.
|
|
"""
|
|
filename = os.path.join(BPFTOOL_BASHCOMP_DIR, 'bpftool')
|
|
|
|
def get_prog_attach_types(self):
|
|
return self.get_bashcomp_list('BPFTOOL_PROG_ATTACH_TYPES')
|
|
|
|
def verify(first_set, second_set, message):
|
|
"""
|
|
Print all values that differ between two sets.
|
|
@first_set: one set to compare
|
|
@second_set: another set to compare
|
|
@message: message to print for values belonging to only one of the sets
|
|
"""
|
|
global retval
|
|
diff = first_set.symmetric_difference(second_set)
|
|
if diff:
|
|
print(message, diff)
|
|
retval = 1
|
|
|
|
def main():
|
|
# No arguments supported at this time, but print usage for -h|--help
|
|
argParser = argparse.ArgumentParser(description="""
|
|
Verify that bpftool's code, help messages, documentation and bash
|
|
completion are all in sync on program types, map types, attach types, and
|
|
options. Also check that bpftool is in sync with the UAPI BPF header.
|
|
""")
|
|
args = argParser.parse_args()
|
|
|
|
bpf_info = BpfHeaderExtractor()
|
|
|
|
# Map types (names)
|
|
|
|
map_info = MapFileExtractor()
|
|
source_map_types = set(bpf_info.get_map_type_map().values())
|
|
source_map_types.discard('unspec')
|
|
|
|
# BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED and BPF_MAP_TYPE_CGROUP_STORAGE
|
|
# share the same enum value and source_map_types picks
|
|
# BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED/cgroup_storage_deprecated.
|
|
# Replace 'cgroup_storage_deprecated' with 'cgroup_storage'
|
|
# so it aligns with what `bpftool map help` shows.
|
|
source_map_types.remove('cgroup_storage_deprecated')
|
|
source_map_types.add('cgroup_storage')
|
|
|
|
# The same applied to BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED and
|
|
# BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE which share the same enum value
|
|
# and source_map_types picks
|
|
# BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED/percpu_cgroup_storage_deprecated.
|
|
# Replace 'percpu_cgroup_storage_deprecated' with 'percpu_cgroup_storage'
|
|
# so it aligns with what `bpftool map help` shows.
|
|
source_map_types.remove('percpu_cgroup_storage_deprecated')
|
|
source_map_types.add('percpu_cgroup_storage')
|
|
|
|
help_map_types = map_info.get_map_help()
|
|
help_map_options = map_info.get_options()
|
|
map_info.close()
|
|
|
|
man_map_info = ManMapExtractor()
|
|
man_map_options = man_map_info.get_options()
|
|
man_map_types = man_map_info.get_map_types()
|
|
man_map_info.close()
|
|
|
|
verify(source_map_types, help_map_types,
|
|
f'Comparing {BpfHeaderExtractor.filename} (bpf_map_type) and {MapFileExtractor.filename} (do_help() TYPE):')
|
|
verify(source_map_types, man_map_types,
|
|
f'Comparing {BpfHeaderExtractor.filename} (bpf_map_type) and {ManMapExtractor.filename} (TYPE):')
|
|
verify(help_map_options, man_map_options,
|
|
f'Comparing {MapFileExtractor.filename} (do_help() OPTIONS) and {ManMapExtractor.filename} (OPTIONS):')
|
|
|
|
# Attach types (names)
|
|
|
|
prog_info = ProgFileExtractor()
|
|
source_prog_attach_types = set(prog_info.get_attach_types().values())
|
|
|
|
help_prog_attach_types = prog_info.get_prog_attach_help()
|
|
help_prog_options = prog_info.get_options()
|
|
prog_info.close()
|
|
|
|
man_prog_info = ManProgExtractor()
|
|
man_prog_options = man_prog_info.get_options()
|
|
man_prog_attach_types = man_prog_info.get_attach_types()
|
|
man_prog_info.close()
|
|
|
|
|
|
bashcomp_info = BashcompExtractor()
|
|
bashcomp_prog_attach_types = bashcomp_info.get_prog_attach_types()
|
|
bashcomp_info.close()
|
|
|
|
verify(source_prog_attach_types, help_prog_attach_types,
|
|
f'Comparing {ProgFileExtractor.filename} (bpf_attach_type) and {ProgFileExtractor.filename} (do_help() ATTACH_TYPE):')
|
|
verify(source_prog_attach_types, man_prog_attach_types,
|
|
f'Comparing {ProgFileExtractor.filename} (bpf_attach_type) and {ManProgExtractor.filename} (ATTACH_TYPE):')
|
|
verify(help_prog_options, man_prog_options,
|
|
f'Comparing {ProgFileExtractor.filename} (do_help() OPTIONS) and {ManProgExtractor.filename} (OPTIONS):')
|
|
verify(source_prog_attach_types, bashcomp_prog_attach_types,
|
|
f'Comparing {ProgFileExtractor.filename} (bpf_attach_type) and {BashcompExtractor.filename} (BPFTOOL_PROG_ATTACH_TYPES):')
|
|
|
|
# Cgroup attach types
|
|
source_cgroup_attach_types = set(bpf_info.get_cgroup_attach_type_map().values())
|
|
bpf_info.close()
|
|
|
|
cgroup_info = CgroupFileExtractor()
|
|
help_cgroup_attach_types = cgroup_info.get_prog_attach_help()
|
|
help_cgroup_options = cgroup_info.get_options()
|
|
cgroup_info.close()
|
|
|
|
man_cgroup_info = ManCgroupExtractor()
|
|
man_cgroup_options = man_cgroup_info.get_options()
|
|
man_cgroup_attach_types = man_cgroup_info.get_attach_types()
|
|
man_cgroup_info.close()
|
|
|
|
verify(source_cgroup_attach_types, help_cgroup_attach_types,
|
|
f'Comparing {BpfHeaderExtractor.filename} (bpf_attach_type) and {CgroupFileExtractor.filename} (do_help() ATTACH_TYPE):')
|
|
verify(source_cgroup_attach_types, man_cgroup_attach_types,
|
|
f'Comparing {BpfHeaderExtractor.filename} (bpf_attach_type) and {ManCgroupExtractor.filename} (ATTACH_TYPE):')
|
|
verify(help_cgroup_options, man_cgroup_options,
|
|
f'Comparing {CgroupFileExtractor.filename} (do_help() OPTIONS) and {ManCgroupExtractor.filename} (OPTIONS):')
|
|
|
|
# Options for remaining commands
|
|
|
|
for cmd in [ 'btf', 'feature', 'gen', 'iter', 'link', 'net', 'perf', 'struct_ops', ]:
|
|
source_info = GenericSourceExtractor(cmd + '.c')
|
|
help_cmd_options = source_info.get_options()
|
|
source_info.close()
|
|
|
|
man_cmd_info = ManGenericExtractor(os.path.join(BPFTOOL_DOC_DIR, 'bpftool-' + cmd + '.rst'))
|
|
man_cmd_options = man_cmd_info.get_options()
|
|
man_cmd_info.close()
|
|
|
|
verify(help_cmd_options, man_cmd_options,
|
|
f'Comparing {source_info.filename} (do_help() OPTIONS) and {man_cmd_info.filename} (OPTIONS):')
|
|
|
|
source_main_info = GenericSourceExtractor('main.c')
|
|
help_main_options = source_main_info.get_options()
|
|
source_main_info.close()
|
|
|
|
man_main_info = ManGenericExtractor(os.path.join(BPFTOOL_DOC_DIR, 'bpftool.rst'))
|
|
man_main_options = man_main_info.get_options()
|
|
man_main_info.close()
|
|
|
|
verify(help_main_options, man_main_options,
|
|
f'Comparing {source_main_info.filename} (do_help() OPTIONS) and {man_main_info.filename} (OPTIONS):')
|
|
|
|
# Compare common options (options that apply to all commands)
|
|
|
|
main_hdr_info = MainHeaderFileExtractor()
|
|
source_common_options = main_hdr_info.get_common_options()
|
|
main_hdr_info.close()
|
|
|
|
man_substitutions = ManSubstitutionsExtractor()
|
|
man_common_options = man_substitutions.get_common_options()
|
|
man_substitutions.close()
|
|
|
|
verify(source_common_options, man_common_options,
|
|
f'Comparing common options from {main_hdr_info.filename} (HELP_SPEC_OPTIONS) and {man_substitutions.filename}:')
|
|
|
|
sys.exit(retval)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|