From 4503fccb7ab07c190902618cfab6f6559c611a95 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Fri, 22 Jun 2018 07:27:54 +0200 Subject: Simplify the find_symbol and list_undefined examples Much of the functionality is available in Kconfiglib itself now. Use the new APIs to implement the examples in a much simpler way. --- examples/find_symbol.py | 257 +++++++++++++++++---------------------------- examples/list_undefined.py | 48 +++++---- 2 files changed, 124 insertions(+), 181 deletions(-) (limited to 'examples') diff --git a/examples/find_symbol.py b/examples/find_symbol.py index adc4d73..0d3c968 100644 --- a/examples/find_symbol.py +++ b/examples/find_symbol.py @@ -1,6 +1,5 @@ -# Prints all symbols, choices, menus, and comments that reference a symbol with -# a particular name in any of their properties or property conditions. -# Demonstrates expression fetching and walking. +# Prints all menu nodes that reference a given symbol any of their properties +# or property conditions, along with their parent menu nodes. # # Usage: # @@ -8,192 +7,124 @@ # # Example output for SCRIPT_ARG=X86: # +# Found 470 locations that reference X86: +# +# ========== Location 1 (init/Kconfig:1108) ========== # -# Found 452 locations that reference 'X86': -# -# ========== Location 1 (init/Kconfig:1122) ========== -# # config SGETMASK_SYSCALL -# bool -# prompt "sgetmask/ssetmask syscalls support" if EXPERT -# default PARISC || MN10300 || BLACKFIN || M68K || PPC || MIPS || X86 || SPARC || CRIS || MICROBLAZE || SUPERH -# help -# sys_sgetmask and sys_ssetmask are obsolete system calls -# no longer supported in libc but still enabled by default in some -# architectures. -# -# If unsure, leave the default option here. -# -# ---------- Parent 1 (init/Kconfig:1091) ---------- -# +# bool +# prompt "sgetmask/ssetmask syscalls support" if EXPERT +# default PARISC || M68K || PPC || MIPS || X86 || SPARC || MICROBLAZE || SUPERH +# help +# sys_sgetmask and sys_ssetmask are obsolete system calls +# no longer supported in libc but still enabled by default in some +# architectures. +# +# If unsure, leave the default option here. +# +# ---------- Parent 1 (init/Kconfig:1077) ---------- +# # menuconfig EXPERT -# bool -# prompt "Configure standard kernel features (expert users)" -# select DEBUG_KERNEL -# help -# This option allows certain base kernel options and settings -# to be disabled or tweaked. This is for specialized -# environments which can tolerate a "non-standard" kernel. -# Only use this if you really know what you are doing. -# +# bool +# prompt "Configure standard kernel features (expert users)" +# select DEBUG_KERNEL +# help +# This option allows certain base kernel options and settings +# to be disabled or tweaked. This is for specialized +# environments which can tolerate a "non-standard" kernel. +# Only use this if you really know what you are doing. +# # ---------- Parent 2 (init/Kconfig:39) ---------- -# +# # menu "General setup" -# -# ========== Location 2 (arch/Kconfig:28) ========== -# +# +# ========== Location 2 (arch/Kconfig:29) ========== +# # config OPROFILE_EVENT_MULTIPLEX -# bool -# prompt "OProfile multiplexing support (EXPERIMENTAL)" if OPROFILE && X86 -# default "n" if OPROFILE && X86 -# help -# The number of hardware counters is limited. The multiplexing -# feature enables OProfile to gather more events than counters -# are provided by the hardware. This is realized by switching -# between events at a user specified time interval. -# -# If unsure, say N. -# -# ---------- Parent 1 (arch/Kconfig:15) ---------- -# +# bool +# prompt "OProfile multiplexing support (EXPERIMENTAL)" if OPROFILE && X86 +# default "n" if OPROFILE && X86 +# depends on OPROFILE && X86 +# help +# The number of hardware counters is limited. The multiplexing +# feature enables OProfile to gather more events than counters +# are provided by the hardware. This is realized by switching +# between events at a user specified time interval. +# +# If unsure, say N. +# +# ---------- Parent 1 (arch/Kconfig:16) ---------- +# # config OPROFILE -# ... (tons more lines) +# tristate +# prompt "OProfile system profiling" if PROFILING && HAVE_OPROFILE +# select RING_BUFFER if PROFILING && HAVE_OPROFILE +# select RING_BUFFER_ALLOW_SWAP if PROFILING && HAVE_OPROFILE +# depends on PROFILING && HAVE_OPROFILE +# help +# OProfile is a profiling system capable of profiling the +# whole system, include the kernel, kernel modules, libraries, +# and applications. +# +# If unsure, say N. +# +# ---------- Parent 2 (init/Kconfig:39) ---------- +# +# menu "General setup" +# +# ... (tons more) -from kconfiglib import Kconfig, Symbol, expr_items, Choice, MENU, COMMENT, NOT import sys -def expr_contains_sym(expr, sym_name): - """ - Returns True if a symbol (or choice, though that's unlikely) with name - 'sym_name' appears in the expression 'expr', and False otherwise. - - Note that "foo" is represented as a constant symbol, like in the C - implementation. - """ - for item in expr_items(expr): - if item.name == sym_name: - return True - - return False - -def sc_references_sym(sc, sym_name): - """ - Returns True if a symbol with name 'sym_name' appears in any of the - properties or property conditions of the Symbol or Choice 'sc', and False - otherwise. - """ - # Search defaults - for default, cond in sc.defaults: - if expr_contains_sym(default, sym_name) or \ - expr_contains_sym(cond, sym_name): - return True - - if isinstance(sc, Symbol): - # Search selects - for select, cond in sc.selects: - if select.name == sym_name or \ - expr_contains_sym(cond, sym_name): - return True - - # Search implies - for imply, cond in sc.implies: - if imply.name == sym_name or \ - expr_contains_sym(cond, sym_name): - return True - - # Search ranges - for low, high, cond in sc.ranges: - if low.name == sym_name or \ - high.name == sym_name or \ - expr_contains_sym(cond, sym_name): - return True - - return False - -def node_references_sym(node, sym_name): - """ - Returns True if a symbol with name 'sym_name' appears in the prompt - condition of the MenuNode 'node' or in any of the properties of a - symbol/choice stored in the menu node, and False otherwise. - - For MENU menu nodes, also searches the 'visible if' condition. - - Note that prompts are always stored in menu nodes. This is why a symbol can - be defined in multiple locations and have a different prompt in each - location. For MENU and COMMENT menu nodes, the prompt holds the menu title - or comment text. This organization matches the C implementation. - """ - if node.prompt: - # Search the prompt condition - if expr_contains_sym(node.prompt[1], sym_name): - return True - - if isinstance(node.item, (Symbol, Choice)): - # Search symbol or choice - return sc_references_sym(node.item, sym_name) - - if node.item == MENU: - # Search the 'visible if' condition - return expr_contains_sym(node.visibility, sym_name) - - # Comments are already handled by searching the prompt condition, because - # 'depends on' gets propagated to it. This is why we don't need to look at - # the direct dependencies for MENU either. - -def nodes_referencing_sym(node, sym_name): - """ - Returns a list of all menu nodes in the menu tree rooted at 'node' that - reference a symbol with name 'sym_name' in any of their properties. Also - checks the properties of any symbols or choices contained in the menu - nodes. - """ +import kconfiglib + + +def referencing_nodes(node, sym): + # Returns a list of all menu nodes that reference 'sym' in any of their + # properties or property conditions + res = [] while node: - if node_references_sym(node, sym_name): + if sym in node.referenced: res.append(node) if node.list: - res.extend(nodes_referencing_sym(node.list, sym_name)) + res.extend(referencing_nodes(node.list, sym)) node = node.next return res -# find_undefined.py makes use nodes_referencing_sym(), so allow use to be -# imported -if __name__ == "__main__": - if len(sys.argv) < 3: - sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=') - - sym_name = sys.argv[2] - kconf = Kconfig(sys.argv[1]) - nodes = nodes_referencing_sym(kconf.top_node, sym_name) +if len(sys.argv) < 3: + sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=') - if not nodes: - sys.exit("No reference to '{}' found".format(sym_name)) +kconf = kconfiglib.Kconfig(sys.argv[1]) +sym_name = sys.argv[2] - print("Found {} locations that reference '{}':\n".format(len(nodes), sym_name)) +if sym_name not in kconf.syms: + print("No symbol {} exists in the configuration".format(sym_name)) + sys.exit(0) - for i, node in enumerate(nodes, 1): - print("========== Location {} ({}:{}) ==========\n".format(i, node.filename, node.linenr)) - print(node) +nodes = referencing_nodes(kconf.top_node, kconf.syms[sym_name]) +if not nodes: + print("No reference to {} found".format(sym_name)) + sys.exit(0) - parent_i = 0 +print("Found {} locations that reference {}:\n" + .format(len(nodes), sym_name)) - # Print the parents of the menu node too - while True: - node = node.parent - if node is kconf.top_node: - # Don't print the top node. Would say something like the - # following, which isn't that interesting: - # - # menu "Linux/$ARCH $KERNELVERSION Kernel Configuration" - break +for i, node in enumerate(nodes, 1): + print("========== Location {} ({}:{}) ==========\n\n{}" + .format(i, node.filename, node.linenr, node)) - parent_i += 1 + # Print the parents of the menu node too - print("---------- Parent {} ({}:{}) ----------\n" - .format(parent_i, node.filename, node.linenr)) - print(node) + node = node.parent + parent_i = 1 + while node is not kconf.top_node: + print("---------- Parent {} ({}:{}) ----------\n\n{}" + .format(parent_i, node.filename, node.linenr, node)) + node = node.parent + parent_i += 1 diff --git a/examples/list_undefined.py b/examples/list_undefined.py index 56d1e4a..0207975 100644 --- a/examples/list_undefined.py +++ b/examples/list_undefined.py @@ -6,7 +6,7 @@ # the symbol, it usually indicates a problem or potential cleanup. # # This script could be sped up a lot if needed. See the comment near the -# nodes_referencing_sym() call. +# referencing_nodes() call. # # Run with the following command in the kernel root: # @@ -35,19 +35,19 @@ # SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14 # AC97: sound/ac97/Kconfig:6 # ... -from kconfiglib import Kconfig - -# Reuse a function from the find_symbol.py example -from find_symbol import nodes_referencing_sym import os import subprocess +from kconfiglib import Kconfig + + # Referenced inside the Kconfig files os.environ["KERNELVERSION"] = str( subprocess.check_output(("make", "kernelversion")).decode("utf-8").rstrip() ) + def all_arch_srcarch_pairs(): """ Generates all valid (ARCH, SRCARCH) tuples for the kernel, corresponding to @@ -75,6 +75,7 @@ def all_arch_srcarch_pairs(): yield ("um", "um") + def all_arch_srcarch_kconfigs(): """ Generates Kconfig instances for all the architectures in the kernel @@ -105,35 +106,46 @@ for kconf in all_arch_srcarch_kconfigs(): else: # Undefined symbol. We skip some of the uninteresting ones. - # Predefined - if name == "UNAME_RELEASE": - continue - # Due to how Kconfig works, integer literals show up as symbols # (from e.g. 'default 1'). Skip those. try: int(name, 0) continue except ValueError: - pass - - # Interesting undefined symbol - undefined.add(name) + # Interesting undefined symbol + undefined.add(name) print("\nFinding references to each undefined symbol") -# Maps each globally undefined symbol to the locations of the items (symbols, -# choices, menus, ifs) that reference it +def referencing_nodes(node, name): + # Returns a list of all menu nodes that reference a symbol named 'name' in + # any of their properties or property conditions + res = [] + + while node: + for ref in node.referenced: + if ref.name == name: + res.append(node) + + if node.list: + res.extend(referencing_nodes(node.list, name)) + + node = node.next + + return res + + +# Maps each globally undefined symbol to the menu nodes that reference it undef_sym_refs = [(name, set()) for name in undefined - defined] for kconf in all_arch_srcarch_kconfigs(): for name, refs in undef_sym_refs: # This means that we search the entire configuration tree for each # undefined symbol, which is terribly inefficient. We could speed - # things up by tweaking nodes_referencing_sym() to compare each symbol - # to multiple symbols while walking the configuration tree. - for node in nodes_referencing_sym(kconf.top_node, name): + # things up by tweaking referencing_nodes() to compare each symbol to + # multiple symbols while walking the configuration tree. + for node in referencing_nodes(kconf.top_node, name): refs.add("{}:{}".format(node.filename, node.linenr)) -- cgit v1.2.3