aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorValentin Rothberg <valentinrothberg@gmail.com>2014-09-27 10:30:45 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-07 12:55:27 -0500
commit24fe1f03e4ef838adb21205d004ec2c3396a3ad9 (patch)
tree292bc267f6eb33a269808dcde0ee9feac37f5c5f /scripts
parent49af54ff0fc7abb6457caed982ddfcfda487e3f7 (diff)
checkkconfigsymbols.sh: reimplementation in python
The scripts/checkkconfigsymbols.sh script searches Kconfig features in the source code that are not defined in Kconfig. Such identifiers always evaluate to false and are the source of various kinds of bugs. However, the shell script is slow and it does not detect such broken references in Kbuild and Kconfig files (e.g., ``depends on UNDEFINED´´). Furthermore, it generates false positives. The script is also hard to read and understand, and is thereby difficult to maintain. This patch replaces the shell script with an implementation in Python, which: (a) detects the same bugs, but does not report previous false positives (b) additionally detects broken references in Kconfig and all non-Kconfig files, such as Kbuild, .[cSh], .txt, .sh, defconfig, etc. (c) is up to 75 times faster than the shell script (d) only checks files under version control The new script reduces the runtime on my machine (i7-2620M, 8GB RAM, SSD) from 3m47s to 0m3s, and reports 938 broken references in Linux v3.17-rc1; 419 additional reports of which 16 are located in Kconfig files, 287 in defconfigs, 63 in ./Documentation, 1 in Kbuild. Moreover, we intentionally include references in comments, which have been ignored until now. Such comments may be leftovers of features that have been removed or renamed in Kconfig (e.g., ``#endif /* CONFIG_MPC52xx */´´). These references can be misleading and should be removed or replaced. Note that the output format changed from (file list <tab> feature) to (feature <tab> file list) as it simplifies the detection of the Kconfig feature for long file lists. Signed-off-by: Valentin Rothberg <valentinrothberg@gmail.com> Signed-off-by: Stefan Hengelein <stefan.hengelein@fau.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/checkkconfigsymbols.py142
-rwxr-xr-xscripts/checkkconfigsymbols.sh59
2 files changed, 142 insertions, 59 deletions
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py
new file mode 100644
index 000000000000..f9440891d048
--- /dev/null
+++ b/scripts/checkkconfigsymbols.py
@@ -0,0 +1,142 @@
1#!/usr/bin/env python
2
3"""Find Kconfig identifieres that are referenced but not defined."""
4
5# Copyright (C) 2014 Valentin Rothberg <valentinrothberg@gmail.com>
6# Copyright (C) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
7#
8# This program is free software; you can redistribute it and/or modify it
9# under the terms and conditions of the GNU General Public License,
10# version 2, as published by the Free Software Foundation.
11#
12# This program is distributed in the hope it will be useful, but WITHOUT
13# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15# more details.
16
17
18import os
19import re
20from subprocess import Popen, PIPE, STDOUT
21
22# REGEX EXPRESSIONS
23OPERATORS = r"&|\(|\)|\||\!"
24FEATURE = r"\w*[A-Z]{1}\w*"
25CONFIG_DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*"
26EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+"
27STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR
28
29# REGEX OBJECTS
30REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
31REGEX_FEATURE = re.compile(r"(" + FEATURE + r")")
32REGEX_SOURCE_FEATURE = re.compile(r"(?:D|\W|\b)+CONFIG_(" + FEATURE + r")")
33REGEX_KCONFIG_DEF = re.compile(CONFIG_DEF)
34REGEX_KCONFIG_EXPR = re.compile(EXPR)
35REGEX_KCONFIG_STMT = re.compile(STMT)
36REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
37REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
38
39
40def main():
41 """Main function of this module."""
42 source_files = []
43 kconfig_files = []
44 defined_features = set()
45 referenced_features = dict()
46
47 # use 'git ls-files' to get the worklist
48 pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True)
49 (stdout, _) = pop.communicate() # wait until finished
50 if len(stdout) > 0 and stdout[-1] == "\n":
51 stdout = stdout[:-1]
52
53 for gitfile in stdout.rsplit("\n"):
54 if ".git" in gitfile or "ChangeLog" in gitfile or \
55 os.path.isdir(gitfile):
56 continue
57 if REGEX_FILE_KCONFIG.match(gitfile):
58 kconfig_files.append(gitfile)
59 else:
60 # All non-Kconfig files are checked for consistency
61 source_files.append(gitfile)
62
63 for sfile in source_files:
64 parse_source_file(sfile, referenced_features)
65
66 for kfile in kconfig_files:
67 parse_kconfig_file(kfile, defined_features, referenced_features)
68
69 print "Undefined symbol used\tFile list"
70 for feature in sorted(referenced_features):
71 if feature not in defined_features:
72 if feature.endswith("_MODULE"):
73 # Avoid false positives for kernel modules
74 if feature[:-len("_MODULE")] in defined_features:
75 continue
76 if "FOO" in feature or "BAR" in feature:
77 continue
78 files = referenced_features.get(feature)
79 print "%s:\t%s" % (feature, ", ".join(files))
80
81
82def parse_source_file(sfile, referenced_features):
83 """Parse @sfile for referenced Kconfig features."""
84 lines = []
85 with open(sfile, "r") as stream:
86 lines = stream.readlines()
87
88 for line in lines:
89 if not "CONFIG_" in line:
90 continue
91 features = REGEX_SOURCE_FEATURE.findall(line)
92 for feature in features:
93 if not REGEX_FILTER_FEATURES.search(feature):
94 continue
95 paths = referenced_features.get(feature, set())
96 paths.add(sfile)
97 referenced_features[feature] = paths
98
99
100def get_features_in_line(line):
101 """Return mentioned Kconfig features in @line."""
102 return REGEX_FEATURE.findall(line)
103
104
105def parse_kconfig_file(kfile, defined_features, referenced_features):
106 """Parse @kfile and update feature definitions and references."""
107 lines = []
108 skip = False
109
110 with open(kfile, "r") as stream:
111 lines = stream.readlines()
112
113 for i in range(len(lines)):
114 line = lines[i]
115 line = line.strip('\n')
116 line = line.split("#")[0] # Ignore Kconfig comments
117
118 if REGEX_KCONFIG_DEF.match(line):
119 feature_def = REGEX_KCONFIG_DEF.findall(line)
120 defined_features.add(feature_def[0])
121 skip = False
122 elif REGEX_KCONFIG_HELP.match(line):
123 skip = True
124 elif skip:
125 # Ignore content of help messages
126 pass
127 elif REGEX_KCONFIG_STMT.match(line):
128 features = get_features_in_line(line)
129 # Multi-line statements
130 while line.endswith("\\"):
131 i += 1
132 line = lines[i]
133 line = line.strip('\n')
134 features.extend(get_features_in_line(line))
135 for feature in set(features):
136 paths = referenced_features.get(feature, set())
137 paths.add(kfile)
138 referenced_features[feature] = paths
139
140
141if __name__ == "__main__":
142 main()
diff --git a/scripts/checkkconfigsymbols.sh b/scripts/checkkconfigsymbols.sh
deleted file mode 100755
index ccb3391882d1..000000000000
--- a/scripts/checkkconfigsymbols.sh
+++ /dev/null
@@ -1,59 +0,0 @@
1#!/bin/sh
2# Find Kconfig variables used in source code but never defined in Kconfig
3# Copyright (C) 2007, Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
4
5# Tested with dash.
6paths="$@"
7[ -z "$paths" ] && paths=.
8
9# Doing this once at the beginning saves a lot of time, on a cache-hot tree.
10Kconfigs="`find . -name 'Kconfig' -o -name 'Kconfig*[^~]'`"
11
12printf "File list \tundefined symbol used\n"
13find $paths -name '*.[chS]' -o -name 'Makefile' -o -name 'Makefile*[^~]'| while read i
14do
15 # Output the bare Kconfig variable and the filename; the _MODULE part at
16 # the end is not removed here (would need perl an not-hungry regexp for that).
17 sed -ne 's!^.*\<\(UML_\)\?CONFIG_\([0-9A-Za-z_]\+\).*!\2 '$i'!p' < $i
18done | \
19# Smart "sort|uniq" implemented in awk and tuned to collect the names of all
20# files which use a given symbol
21awk '{map[$1, count[$1]++] = $2; }
22END {
23 for (combIdx in map) {
24 split(combIdx, separate, SUBSEP);
25 # The value may have been removed.
26 if (! ( (separate[1], separate[2]) in map ) )
27 continue;
28 symb=separate[1];
29 printf "%s ", symb;
30 #Use gawk extension to delete the names vector
31 delete names;
32 #Portably delete the names vector
33 #split("", names);
34 for (i=0; i < count[symb]; i++) {
35 names[map[symb, i]] = 1;
36 # Unfortunately, we may still encounter symb, i in the
37 # outside iteration.
38 delete map[symb, i];
39 }
40 i=0;
41 for (name in names) {
42 if (i > 0)
43 printf ", %s", name;
44 else
45 printf "%s", name;
46 i++;
47 }
48 printf "\n";
49 }
50}' |
51while read symb files; do
52 # Remove the _MODULE suffix when checking the variable name. This should
53 # be done only on tristate symbols, actually, but Kconfig parsing is
54 # beyond the purpose of this script.
55 symb_bare=`echo $symb | sed -e 's/_MODULE//'`
56 if ! grep -q "\<$symb_bare\>" $Kconfigs; then
57 printf "$files: \t$symb\n"
58 fi
59done|sort