mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
		
			
				
	
	
		
			214 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			214 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()
 |