1484e9aa6SMauro Carvalho Chehab#!/usr/bin/env python3 2484e9aa6SMauro Carvalho Chehab# pylint: disable=R0903 3484e9aa6SMauro Carvalho Chehab# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4484e9aa6SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 5484e9aa6SMauro Carvalho Chehab 6484e9aa6SMauro Carvalho Chehab""" 7484e9aa6SMauro Carvalho ChehabParse ABI documentation and produce results from it. 8484e9aa6SMauro Carvalho Chehab""" 9484e9aa6SMauro Carvalho Chehab 10484e9aa6SMauro Carvalho Chehabimport argparse 11484e9aa6SMauro Carvalho Chehabimport logging 12484e9aa6SMauro Carvalho Chehabimport os 13484e9aa6SMauro Carvalho Chehabimport sys 14484e9aa6SMauro Carvalho Chehab 15484e9aa6SMauro Carvalho Chehab# Import Python modules 16484e9aa6SMauro Carvalho Chehab 17484e9aa6SMauro Carvalho ChehabLIB_DIR = "lib/abi" 18484e9aa6SMauro Carvalho ChehabSRC_DIR = os.path.dirname(os.path.realpath(__file__)) 19484e9aa6SMauro Carvalho Chehab 20484e9aa6SMauro Carvalho Chehabsys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 21484e9aa6SMauro Carvalho Chehab 22484e9aa6SMauro Carvalho Chehabfrom abi_parser import AbiParser # pylint: disable=C0413 23*0d5fd968SMauro Carvalho Chehabfrom abi_regex import AbiRegex # pylint: disable=C0413 24484e9aa6SMauro Carvalho Chehabfrom helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413 25*0d5fd968SMauro Carvalho Chehabfrom system_symbols import SystemSymbols # pylint: disable=C0413 26484e9aa6SMauro Carvalho Chehab 27484e9aa6SMauro Carvalho Chehab# Command line classes 28484e9aa6SMauro Carvalho Chehab 29484e9aa6SMauro Carvalho Chehab 30484e9aa6SMauro Carvalho ChehabREST_DESC = """ 31484e9aa6SMauro Carvalho ChehabProduce output in ReST format. 32484e9aa6SMauro Carvalho Chehab 33484e9aa6SMauro Carvalho ChehabThe output is done on two sections: 34484e9aa6SMauro Carvalho Chehab 35484e9aa6SMauro Carvalho Chehab- Symbols: show all parsed symbols in alphabetic order; 36484e9aa6SMauro Carvalho Chehab- Files: cross reference the content of each file with the symbols on it. 37484e9aa6SMauro Carvalho Chehab""" 38484e9aa6SMauro Carvalho Chehab 39484e9aa6SMauro Carvalho Chehabclass AbiRest: 40484e9aa6SMauro Carvalho Chehab """Initialize an argparse subparser for rest output""" 41484e9aa6SMauro Carvalho Chehab 42484e9aa6SMauro Carvalho Chehab def __init__(self, subparsers): 43484e9aa6SMauro Carvalho Chehab """Initialize argparse subparsers""" 44484e9aa6SMauro Carvalho Chehab 45484e9aa6SMauro Carvalho Chehab parser = subparsers.add_parser("rest", 46484e9aa6SMauro Carvalho Chehab formatter_class=argparse.RawTextHelpFormatter, 47484e9aa6SMauro Carvalho Chehab description=REST_DESC) 48484e9aa6SMauro Carvalho Chehab 49484e9aa6SMauro Carvalho Chehab parser.add_argument("--enable-lineno", action="store_true", 50484e9aa6SMauro Carvalho Chehab help="enable lineno") 51484e9aa6SMauro Carvalho Chehab parser.add_argument("--raw", action="store_true", 52484e9aa6SMauro Carvalho Chehab help="output text as contained in the ABI files. " 53484e9aa6SMauro Carvalho Chehab "It not used, output will contain dynamically" 54484e9aa6SMauro Carvalho Chehab " generated cross references when possible.") 55484e9aa6SMauro Carvalho Chehab parser.add_argument("--no-file", action="store_true", 56484e9aa6SMauro Carvalho Chehab help="Don't the files section") 57484e9aa6SMauro Carvalho Chehab parser.add_argument("--show-hints", help="Show-hints") 58484e9aa6SMauro Carvalho Chehab 59484e9aa6SMauro Carvalho Chehab parser.set_defaults(func=self.run) 60484e9aa6SMauro Carvalho Chehab 61484e9aa6SMauro Carvalho Chehab def run(self, args): 62484e9aa6SMauro Carvalho Chehab """Run subparser""" 63484e9aa6SMauro Carvalho Chehab 64484e9aa6SMauro Carvalho Chehab parser = AbiParser(args.dir, debug=args.debug) 65484e9aa6SMauro Carvalho Chehab parser.parse_abi() 66484e9aa6SMauro Carvalho Chehab parser.check_issues() 67484e9aa6SMauro Carvalho Chehab 68aea5e52dSMauro Carvalho Chehab for t in parser.doc(args.raw, not args.no_file): 69aea5e52dSMauro Carvalho Chehab if args.enable_lineno: 70aea5e52dSMauro Carvalho Chehab print (f".. LINENO {t[1]}#{t[2]}\n\n") 71aea5e52dSMauro Carvalho Chehab 72aea5e52dSMauro Carvalho Chehab print(t[0]) 73484e9aa6SMauro Carvalho Chehab 74484e9aa6SMauro Carvalho Chehabclass AbiValidate: 75484e9aa6SMauro Carvalho Chehab """Initialize an argparse subparser for ABI validation""" 76484e9aa6SMauro Carvalho Chehab 77484e9aa6SMauro Carvalho Chehab def __init__(self, subparsers): 78484e9aa6SMauro Carvalho Chehab """Initialize argparse subparsers""" 79484e9aa6SMauro Carvalho Chehab 80484e9aa6SMauro Carvalho Chehab parser = subparsers.add_parser("validate", 81484e9aa6SMauro Carvalho Chehab formatter_class=argparse.ArgumentDefaultsHelpFormatter, 82484e9aa6SMauro Carvalho Chehab description="list events") 83484e9aa6SMauro Carvalho Chehab 84484e9aa6SMauro Carvalho Chehab parser.set_defaults(func=self.run) 85484e9aa6SMauro Carvalho Chehab 86484e9aa6SMauro Carvalho Chehab def run(self, args): 87484e9aa6SMauro Carvalho Chehab """Run subparser""" 88484e9aa6SMauro Carvalho Chehab 89484e9aa6SMauro Carvalho Chehab parser = AbiParser(args.dir, debug=args.debug) 90484e9aa6SMauro Carvalho Chehab parser.parse_abi() 91484e9aa6SMauro Carvalho Chehab parser.check_issues() 92484e9aa6SMauro Carvalho Chehab 93484e9aa6SMauro Carvalho Chehab 946b48bea1SMauro Carvalho Chehabclass AbiSearch: 956b48bea1SMauro Carvalho Chehab """Initialize an argparse subparser for ABI search""" 966b48bea1SMauro Carvalho Chehab 976b48bea1SMauro Carvalho Chehab def __init__(self, subparsers): 986b48bea1SMauro Carvalho Chehab """Initialize argparse subparsers""" 996b48bea1SMauro Carvalho Chehab 1006b48bea1SMauro Carvalho Chehab parser = subparsers.add_parser("search", 1016b48bea1SMauro Carvalho Chehab formatter_class=argparse.ArgumentDefaultsHelpFormatter, 1026b48bea1SMauro Carvalho Chehab description="Search ABI using a regular expression") 1036b48bea1SMauro Carvalho Chehab 1046b48bea1SMauro Carvalho Chehab parser.add_argument("expression", 1056b48bea1SMauro Carvalho Chehab help="Case-insensitive search pattern for the ABI symbol") 1066b48bea1SMauro Carvalho Chehab 1076b48bea1SMauro Carvalho Chehab parser.set_defaults(func=self.run) 1086b48bea1SMauro Carvalho Chehab 1096b48bea1SMauro Carvalho Chehab def run(self, args): 1106b48bea1SMauro Carvalho Chehab """Run subparser""" 1116b48bea1SMauro Carvalho Chehab 1126b48bea1SMauro Carvalho Chehab parser = AbiParser(args.dir, debug=args.debug) 1136b48bea1SMauro Carvalho Chehab parser.parse_abi() 1146b48bea1SMauro Carvalho Chehab parser.search_symbols(args.expression) 1156b48bea1SMauro Carvalho Chehab 116*0d5fd968SMauro Carvalho ChehabUNDEFINED_DESC=""" 117*0d5fd968SMauro Carvalho ChehabCheck undefined ABIs on local machine. 118*0d5fd968SMauro Carvalho Chehab 119*0d5fd968SMauro Carvalho ChehabRead sysfs devnodes and check if the devnodes there are defined inside 120*0d5fd968SMauro Carvalho ChehabABI documentation. 121*0d5fd968SMauro Carvalho Chehab 122*0d5fd968SMauro Carvalho ChehabThe search logic tries to minimize the number of regular expressions to 123*0d5fd968SMauro Carvalho Chehabsearch per each symbol. 124*0d5fd968SMauro Carvalho Chehab 125*0d5fd968SMauro Carvalho ChehabBy default, it runs on a single CPU, as Python support for CPU threads 126*0d5fd968SMauro Carvalho Chehabis still experimental, and multi-process runs on Python is very slow. 127*0d5fd968SMauro Carvalho Chehab 128*0d5fd968SMauro Carvalho ChehabOn experimental tests, if the number of ABI symbols to search per devnode 129*0d5fd968SMauro Carvalho Chehabis contained on a limit of ~150 regular expressions, using a single CPU 130*0d5fd968SMauro Carvalho Chehabis a lot faster than using multiple processes. However, if the number of 131*0d5fd968SMauro Carvalho Chehabregular expressions to check is at the order of ~30000, using multiple 132*0d5fd968SMauro Carvalho ChehabCPUs speeds up the check. 133*0d5fd968SMauro Carvalho Chehab""" 134*0d5fd968SMauro Carvalho Chehab 135*0d5fd968SMauro Carvalho Chehabclass AbiUndefined: 136*0d5fd968SMauro Carvalho Chehab """ 137*0d5fd968SMauro Carvalho Chehab Initialize an argparse subparser for logic to check undefined ABI at 138*0d5fd968SMauro Carvalho Chehab the current machine's sysfs 139*0d5fd968SMauro Carvalho Chehab """ 140*0d5fd968SMauro Carvalho Chehab 141*0d5fd968SMauro Carvalho Chehab def __init__(self, subparsers): 142*0d5fd968SMauro Carvalho Chehab """Initialize argparse subparsers""" 143*0d5fd968SMauro Carvalho Chehab 144*0d5fd968SMauro Carvalho Chehab parser = subparsers.add_parser("undefined", 145*0d5fd968SMauro Carvalho Chehab formatter_class=argparse.RawTextHelpFormatter, 146*0d5fd968SMauro Carvalho Chehab description=UNDEFINED_DESC) 147*0d5fd968SMauro Carvalho Chehab 148*0d5fd968SMauro Carvalho Chehab parser.add_argument("-S", "--sysfs-dir", default="/sys", 149*0d5fd968SMauro Carvalho Chehab help="directory where sysfs is mounted") 150*0d5fd968SMauro Carvalho Chehab parser.add_argument("-s", "--search-string", 151*0d5fd968SMauro Carvalho Chehab help="search string regular expression to limit symbol search") 152*0d5fd968SMauro Carvalho Chehab parser.add_argument("-H", "--show-hints", action="store_true", 153*0d5fd968SMauro Carvalho Chehab help="Hints about definitions for missing ABI symbols.") 154*0d5fd968SMauro Carvalho Chehab parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1, 155*0d5fd968SMauro Carvalho Chehab help="If bigger than one, enables multiprocessing.") 156*0d5fd968SMauro Carvalho Chehab parser.add_argument("-c", "--max-chunk-size", type=int, default=50, 157*0d5fd968SMauro Carvalho Chehab help="Maximum number of chunk size") 158*0d5fd968SMauro Carvalho Chehab parser.add_argument("-f", "--found", action="store_true", 159*0d5fd968SMauro Carvalho Chehab help="Also show found items. " 160*0d5fd968SMauro Carvalho Chehab "Helpful to debug the parser."), 161*0d5fd968SMauro Carvalho Chehab parser.add_argument("-d", "--dry-run", action="store_true", 162*0d5fd968SMauro Carvalho Chehab help="Don't actually search for undefined. " 163*0d5fd968SMauro Carvalho Chehab "Helpful to debug the parser."), 164*0d5fd968SMauro Carvalho Chehab 165*0d5fd968SMauro Carvalho Chehab parser.set_defaults(func=self.run) 166*0d5fd968SMauro Carvalho Chehab 167*0d5fd968SMauro Carvalho Chehab def run(self, args): 168*0d5fd968SMauro Carvalho Chehab """Run subparser""" 169*0d5fd968SMauro Carvalho Chehab 170*0d5fd968SMauro Carvalho Chehab abi = AbiRegex(args.dir, debug=args.debug, 171*0d5fd968SMauro Carvalho Chehab search_string=args.search_string) 172*0d5fd968SMauro Carvalho Chehab 173*0d5fd968SMauro Carvalho Chehab abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints, 174*0d5fd968SMauro Carvalho Chehab sysfs=args.sysfs_dir) 175*0d5fd968SMauro Carvalho Chehab 176*0d5fd968SMauro Carvalho Chehab abi_symbols.check_undefined_symbols(dry_run=args.dry_run, 177*0d5fd968SMauro Carvalho Chehab found=args.found, 178*0d5fd968SMauro Carvalho Chehab max_workers=args.jobs, 179*0d5fd968SMauro Carvalho Chehab chunk_size=args.max_chunk_size) 180*0d5fd968SMauro Carvalho Chehab 1816b48bea1SMauro Carvalho Chehab 182484e9aa6SMauro Carvalho Chehabdef main(): 183484e9aa6SMauro Carvalho Chehab """Main program""" 184484e9aa6SMauro Carvalho Chehab 185484e9aa6SMauro Carvalho Chehab parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) 186484e9aa6SMauro Carvalho Chehab 187484e9aa6SMauro Carvalho Chehab parser.add_argument("-d", "--debug", type=int, default=0, help="debug level") 188484e9aa6SMauro Carvalho Chehab parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP) 189484e9aa6SMauro Carvalho Chehab 190484e9aa6SMauro Carvalho Chehab subparsers = parser.add_subparsers() 191484e9aa6SMauro Carvalho Chehab 192484e9aa6SMauro Carvalho Chehab AbiRest(subparsers) 193484e9aa6SMauro Carvalho Chehab AbiValidate(subparsers) 1946b48bea1SMauro Carvalho Chehab AbiSearch(subparsers) 195*0d5fd968SMauro Carvalho Chehab AbiUndefined(subparsers) 196484e9aa6SMauro Carvalho Chehab 197484e9aa6SMauro Carvalho Chehab args = parser.parse_args() 198484e9aa6SMauro Carvalho Chehab 199484e9aa6SMauro Carvalho Chehab if args.debug: 200484e9aa6SMauro Carvalho Chehab level = logging.DEBUG 201484e9aa6SMauro Carvalho Chehab else: 202484e9aa6SMauro Carvalho Chehab level = logging.INFO 203484e9aa6SMauro Carvalho Chehab 204484e9aa6SMauro Carvalho Chehab logging.basicConfig(level=level, format="[%(levelname)s] %(message)s") 205484e9aa6SMauro Carvalho Chehab 206484e9aa6SMauro Carvalho Chehab if "func" in args: 207484e9aa6SMauro Carvalho Chehab args.func(args) 208484e9aa6SMauro Carvalho Chehab else: 209484e9aa6SMauro Carvalho Chehab sys.exit(f"Please specify a valid command for {sys.argv[0]}") 210484e9aa6SMauro Carvalho Chehab 211484e9aa6SMauro Carvalho Chehab 212484e9aa6SMauro Carvalho Chehab# Call main method 213484e9aa6SMauro Carvalho Chehabif __name__ == "__main__": 214484e9aa6SMauro Carvalho Chehab main() 215