mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00

The undefined logic is complex and has lots of magic on it. Implement it, using the same algorithm we have at get_abi.pl. Yet, some tweaks to optimize performance and to make the code simpler were added here: - at the perl version, the tree graph had loops, so we had to use BFS to traverse it. On this version, the graph is a tree, so, it simplifies the what group for sysfs aliases; - the logic which splits regular expressions into subgroups was re-written to make it faster; - it may optionally use multiple processes to search for symbol matches; - it has some additional debug levels. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Link: https://lore.kernel.org/r/1529c255845d117696d5af57d8dc05554663afdf.1739182025.git.mchehab+huawei@kernel.org
215 lines
7.2 KiB
Python
Executable File
215 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=R0903
|
|
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
"""
|
|
Parse ABI documentation and produce results from it.
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
# Import Python modules
|
|
|
|
LIB_DIR = "lib/abi"
|
|
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
|
|
|
|
from abi_parser import AbiParser # pylint: disable=C0413
|
|
from abi_regex import AbiRegex # pylint: disable=C0413
|
|
from helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413
|
|
from system_symbols import SystemSymbols # pylint: disable=C0413
|
|
|
|
# Command line classes
|
|
|
|
|
|
REST_DESC = """
|
|
Produce output in ReST format.
|
|
|
|
The output is done on two sections:
|
|
|
|
- Symbols: show all parsed symbols in alphabetic order;
|
|
- Files: cross reference the content of each file with the symbols on it.
|
|
"""
|
|
|
|
class AbiRest:
|
|
"""Initialize an argparse subparser for rest output"""
|
|
|
|
def __init__(self, subparsers):
|
|
"""Initialize argparse subparsers"""
|
|
|
|
parser = subparsers.add_parser("rest",
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
description=REST_DESC)
|
|
|
|
parser.add_argument("--enable-lineno", action="store_true",
|
|
help="enable lineno")
|
|
parser.add_argument("--raw", action="store_true",
|
|
help="output text as contained in the ABI files. "
|
|
"It not used, output will contain dynamically"
|
|
" generated cross references when possible.")
|
|
parser.add_argument("--no-file", action="store_true",
|
|
help="Don't the files section")
|
|
parser.add_argument("--show-hints", help="Show-hints")
|
|
|
|
parser.set_defaults(func=self.run)
|
|
|
|
def run(self, args):
|
|
"""Run subparser"""
|
|
|
|
parser = AbiParser(args.dir, debug=args.debug)
|
|
parser.parse_abi()
|
|
parser.check_issues()
|
|
|
|
for t in parser.doc(args.raw, not args.no_file):
|
|
if args.enable_lineno:
|
|
print (f".. LINENO {t[1]}#{t[2]}\n\n")
|
|
|
|
print(t[0])
|
|
|
|
class AbiValidate:
|
|
"""Initialize an argparse subparser for ABI validation"""
|
|
|
|
def __init__(self, subparsers):
|
|
"""Initialize argparse subparsers"""
|
|
|
|
parser = subparsers.add_parser("validate",
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
description="list events")
|
|
|
|
parser.set_defaults(func=self.run)
|
|
|
|
def run(self, args):
|
|
"""Run subparser"""
|
|
|
|
parser = AbiParser(args.dir, debug=args.debug)
|
|
parser.parse_abi()
|
|
parser.check_issues()
|
|
|
|
|
|
class AbiSearch:
|
|
"""Initialize an argparse subparser for ABI search"""
|
|
|
|
def __init__(self, subparsers):
|
|
"""Initialize argparse subparsers"""
|
|
|
|
parser = subparsers.add_parser("search",
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
description="Search ABI using a regular expression")
|
|
|
|
parser.add_argument("expression",
|
|
help="Case-insensitive search pattern for the ABI symbol")
|
|
|
|
parser.set_defaults(func=self.run)
|
|
|
|
def run(self, args):
|
|
"""Run subparser"""
|
|
|
|
parser = AbiParser(args.dir, debug=args.debug)
|
|
parser.parse_abi()
|
|
parser.search_symbols(args.expression)
|
|
|
|
UNDEFINED_DESC="""
|
|
Check undefined ABIs on local machine.
|
|
|
|
Read sysfs devnodes and check if the devnodes there are defined inside
|
|
ABI documentation.
|
|
|
|
The search logic tries to minimize the number of regular expressions to
|
|
search per each symbol.
|
|
|
|
By default, it runs on a single CPU, as Python support for CPU threads
|
|
is still experimental, and multi-process runs on Python is very slow.
|
|
|
|
On experimental tests, if the number of ABI symbols to search per devnode
|
|
is contained on a limit of ~150 regular expressions, using a single CPU
|
|
is a lot faster than using multiple processes. However, if the number of
|
|
regular expressions to check is at the order of ~30000, using multiple
|
|
CPUs speeds up the check.
|
|
"""
|
|
|
|
class AbiUndefined:
|
|
"""
|
|
Initialize an argparse subparser for logic to check undefined ABI at
|
|
the current machine's sysfs
|
|
"""
|
|
|
|
def __init__(self, subparsers):
|
|
"""Initialize argparse subparsers"""
|
|
|
|
parser = subparsers.add_parser("undefined",
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
description=UNDEFINED_DESC)
|
|
|
|
parser.add_argument("-S", "--sysfs-dir", default="/sys",
|
|
help="directory where sysfs is mounted")
|
|
parser.add_argument("-s", "--search-string",
|
|
help="search string regular expression to limit symbol search")
|
|
parser.add_argument("-H", "--show-hints", action="store_true",
|
|
help="Hints about definitions for missing ABI symbols.")
|
|
parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1,
|
|
help="If bigger than one, enables multiprocessing.")
|
|
parser.add_argument("-c", "--max-chunk-size", type=int, default=50,
|
|
help="Maximum number of chunk size")
|
|
parser.add_argument("-f", "--found", action="store_true",
|
|
help="Also show found items. "
|
|
"Helpful to debug the parser."),
|
|
parser.add_argument("-d", "--dry-run", action="store_true",
|
|
help="Don't actually search for undefined. "
|
|
"Helpful to debug the parser."),
|
|
|
|
parser.set_defaults(func=self.run)
|
|
|
|
def run(self, args):
|
|
"""Run subparser"""
|
|
|
|
abi = AbiRegex(args.dir, debug=args.debug,
|
|
search_string=args.search_string)
|
|
|
|
abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints,
|
|
sysfs=args.sysfs_dir)
|
|
|
|
abi_symbols.check_undefined_symbols(dry_run=args.dry_run,
|
|
found=args.found,
|
|
max_workers=args.jobs,
|
|
chunk_size=args.max_chunk_size)
|
|
|
|
|
|
def main():
|
|
"""Main program"""
|
|
|
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
|
|
|
|
parser.add_argument("-d", "--debug", type=int, default=0, help="debug level")
|
|
parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP)
|
|
|
|
subparsers = parser.add_subparsers()
|
|
|
|
AbiRest(subparsers)
|
|
AbiValidate(subparsers)
|
|
AbiSearch(subparsers)
|
|
AbiUndefined(subparsers)
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.debug:
|
|
level = logging.DEBUG
|
|
else:
|
|
level = logging.INFO
|
|
|
|
logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
|
|
|
|
if "func" in args:
|
|
args.func(args)
|
|
else:
|
|
sys.exit(f"Please specify a valid command for {sys.argv[0]}")
|
|
|
|
|
|
# Call main method
|
|
if __name__ == "__main__":
|
|
main()
|