Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/env python3
2# pylint: disable=R0903
3# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
4# SPDX-License-Identifier: GPL-2.0
5
6"""
7Parse ABI documentation and produce results from it.
8"""
9
10import argparse
11import logging
12import os
13import sys
14
15# Import Python modules
16
17LIB_DIR = "lib/abi"
18SRC_DIR = os.path.dirname(os.path.realpath(__file__))
19
20sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
21
22from abi_parser import AbiParser # pylint: disable=C0413
23from abi_regex import AbiRegex # pylint: disable=C0413
24from helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413
25from system_symbols import SystemSymbols # pylint: disable=C0413
26
27# Command line classes
28
29
30REST_DESC = """
31Produce output in ReST format.
32
33The output is done on two sections:
34
35- Symbols: show all parsed symbols in alphabetic order;
36- Files: cross reference the content of each file with the symbols on it.
37"""
38
39class AbiRest:
40 """Initialize an argparse subparser for rest output"""
41
42 def __init__(self, subparsers):
43 """Initialize argparse subparsers"""
44
45 parser = subparsers.add_parser("rest",
46 formatter_class=argparse.RawTextHelpFormatter,
47 description=REST_DESC)
48
49 parser.add_argument("--enable-lineno", action="store_true",
50 help="enable lineno")
51 parser.add_argument("--raw", action="store_true",
52 help="output text as contained in the ABI files. "
53 "It not used, output will contain dynamically"
54 " generated cross references when possible.")
55 parser.add_argument("--no-file", action="store_true",
56 help="Don't the files section")
57 parser.add_argument("--show-hints", help="Show-hints")
58
59 parser.set_defaults(func=self.run)
60
61 def run(self, args):
62 """Run subparser"""
63
64 parser = AbiParser(args.dir, debug=args.debug)
65 parser.parse_abi()
66 parser.check_issues()
67
68 for t in parser.doc(args.raw, not args.no_file):
69 if args.enable_lineno:
70 print (f".. LINENO {t[1]}#{t[2]}\n\n")
71
72 print(t[0])
73
74class AbiValidate:
75 """Initialize an argparse subparser for ABI validation"""
76
77 def __init__(self, subparsers):
78 """Initialize argparse subparsers"""
79
80 parser = subparsers.add_parser("validate",
81 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
82 description="list events")
83
84 parser.set_defaults(func=self.run)
85
86 def run(self, args):
87 """Run subparser"""
88
89 parser = AbiParser(args.dir, debug=args.debug)
90 parser.parse_abi()
91 parser.check_issues()
92
93
94class AbiSearch:
95 """Initialize an argparse subparser for ABI search"""
96
97 def __init__(self, subparsers):
98 """Initialize argparse subparsers"""
99
100 parser = subparsers.add_parser("search",
101 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
102 description="Search ABI using a regular expression")
103
104 parser.add_argument("expression",
105 help="Case-insensitive search pattern for the ABI symbol")
106
107 parser.set_defaults(func=self.run)
108
109 def run(self, args):
110 """Run subparser"""
111
112 parser = AbiParser(args.dir, debug=args.debug)
113 parser.parse_abi()
114 parser.search_symbols(args.expression)
115
116UNDEFINED_DESC="""
117Check undefined ABIs on local machine.
118
119Read sysfs devnodes and check if the devnodes there are defined inside
120ABI documentation.
121
122The search logic tries to minimize the number of regular expressions to
123search per each symbol.
124
125By default, it runs on a single CPU, as Python support for CPU threads
126is still experimental, and multi-process runs on Python is very slow.
127
128On experimental tests, if the number of ABI symbols to search per devnode
129is contained on a limit of ~150 regular expressions, using a single CPU
130is a lot faster than using multiple processes. However, if the number of
131regular expressions to check is at the order of ~30000, using multiple
132CPUs speeds up the check.
133"""
134
135class AbiUndefined:
136 """
137 Initialize an argparse subparser for logic to check undefined ABI at
138 the current machine's sysfs
139 """
140
141 def __init__(self, subparsers):
142 """Initialize argparse subparsers"""
143
144 parser = subparsers.add_parser("undefined",
145 formatter_class=argparse.RawTextHelpFormatter,
146 description=UNDEFINED_DESC)
147
148 parser.add_argument("-S", "--sysfs-dir", default="/sys",
149 help="directory where sysfs is mounted")
150 parser.add_argument("-s", "--search-string",
151 help="search string regular expression to limit symbol search")
152 parser.add_argument("-H", "--show-hints", action="store_true",
153 help="Hints about definitions for missing ABI symbols.")
154 parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1,
155 help="If bigger than one, enables multiprocessing.")
156 parser.add_argument("-c", "--max-chunk-size", type=int, default=50,
157 help="Maximum number of chunk size")
158 parser.add_argument("-f", "--found", action="store_true",
159 help="Also show found items. "
160 "Helpful to debug the parser."),
161 parser.add_argument("-d", "--dry-run", action="store_true",
162 help="Don't actually search for undefined. "
163 "Helpful to debug the parser."),
164
165 parser.set_defaults(func=self.run)
166
167 def run(self, args):
168 """Run subparser"""
169
170 abi = AbiRegex(args.dir, debug=args.debug,
171 search_string=args.search_string)
172
173 abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints,
174 sysfs=args.sysfs_dir)
175
176 abi_symbols.check_undefined_symbols(dry_run=args.dry_run,
177 found=args.found,
178 max_workers=args.jobs,
179 chunk_size=args.max_chunk_size)
180
181
182def main():
183 """Main program"""
184
185 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
186
187 parser.add_argument("-d", "--debug", type=int, default=0, help="debug level")
188 parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP)
189
190 subparsers = parser.add_subparsers()
191
192 AbiRest(subparsers)
193 AbiValidate(subparsers)
194 AbiSearch(subparsers)
195 AbiUndefined(subparsers)
196
197 args = parser.parse_args()
198
199 if args.debug:
200 level = logging.DEBUG
201 else:
202 level = logging.INFO
203
204 logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
205
206 if "func" in args:
207 args.func(args)
208 else:
209 sys.exit(f"Please specify a valid command for {sys.argv[0]}")
210
211
212# Call main method
213if __name__ == "__main__":
214 main()