Kernel cleanup scripts
From Crashcourse Wiki
Here are the scripts I run on occasion to scan the kernel source tree to identify oddities and inconsistencies in the Kconfig structure. In most cases, you run the script from the top of the source tree and you can either specify a sub-directory to scan or let it default to scanning the entire tree, which will probably take a while so you're usually better off to scan only the portion you're interested in.
I did throw these scripts together in a hurry some time back, so I'm always open to suggestions for improvement. I'd actually like to rewrite a number of these scripts in Python, and use a multi-level associative array to build a tree of the CONFIG variables to support more interesting scanning.
Feedback/comments/suggestions to rpjday@crashcourse.ca.
Contents |
[edit] Unused CONFIG variables
This script locates variables that are defined in some Kconfig file (within that part of the tree of interest), but appear to be entirely unused and untested anywhere in the tree. Normally, this isn't a serious problem -- it generally represents leftover cruft from time gone by, although occasionally this scanning identifies obvious spelling mistakes in Kconfig files or in source files that do the testing.
#!/bin/sh
SCAN_DIR=${1-*}
# Collect config variables from Kconfig* files of interest.
KCONFIG_FILES=$(find ${SCAN_DIR} -name Kconfig*)
if [ "${KCONFIG_FILES}" = "" ] ; then
echo "No kconfig files, exiting."
exit 1
fi
ALL_KCONFIG_FILES=$(find * -name Kconfig*)
ALL_MAKE_FILES=$(find * -name Makefile)
KCONFIG_VARS1=$(grep ^config ${KCONFIG_FILES} | awk '{print $2}' | sort)
KCONFIG_VARS2=$(grep ^menuconfig ${KCONFIG_FILES} | awk '{print $2}' | sort)
for kv in ${KCONFIG_VARS1} ${KCONFIG_VARS2} ; do
# echo "Checking ${kv} ..."
grep -q "depends on.*${kv}" ${ALL_KCONFIG_FILES} ||
grep -q "if.*${kv}" ${ALL_KCONFIG_FILES} ||
grep -q "default.*${kv}" ${ALL_KCONFIG_FILES} ||
grep -r -w -q --exclude="*defconfig" "CONFIG_${kv}" * || {
echo "===== ${kv}"
grep -rwn ${kv} * | grep -v defconfig
}
done
Here's a sample run, examining the sub-directory drivers/media/video:
$ find_unused_configs.sh drivers/media/video ===== VIDEO_BT848_DVB drivers/media/video/bt8xx/Kconfig:22:config VIDEO_BT848_DVB ===== VIDEO_ZORAN_AVS6EYES drivers/media/video/zoran/Kconfig:67:config VIDEO_ZORAN_AVS6EYES ===== VIDEO_ZORAN_BUZ drivers/media/video/zoran/Kconfig:32:config VIDEO_ZORAN_BUZ ===== VIDEO_ZORAN_DC10 drivers/media/video/zoran/Kconfig:40:config VIDEO_ZORAN_DC10 ===== VIDEO_ZORAN_LML33 drivers/media/video/zoran/Kconfig:49:config VIDEO_ZORAN_LML33 ===== VIDEO_ZORAN_LML33R10 drivers/media/video/zoran/Kconfig:58:config VIDEO_ZORAN_LML33R10 $
According to the script, all of those variables are defined in a Kconfig file, but aren't tested anywhere. One can verify that by doing a simple recursive grep throughout the entire tree for, say, that first example (or any sufficient substring), to confirm:
$ grep -r BT848_DVB * arch/mips/configs/mtx1_defconfig:CONFIG_VIDEO_BT848_DVB=y arch/powerpc/configs/ppc6xx_defconfig:CONFIG_VIDEO_BT848_DVB=y drivers/media/video/bt8xx/Kconfig:config VIDEO_BT848_DVB $
Certainly looks unused from here.
NOTE: Occasionally, unused CONFIG variables represent the first part of adding a new feature to the kernel, where the Kconfig infrastructure is added while the associated code is supposed to be "coming soon." That's just lame and you know it.
Here's another common result:
$ find_unused_configs.sh drivers/media/radio ===== RADIO_TEA5764_XTAL drivers/media/radio/radio-tea5764.c:132:#ifndef RADIO_TEA5764_XTAL drivers/media/radio/radio-tea5764.c:133:#define RADIO_TEA5764_XTAL 1 drivers/media/radio/radio-tea5764.c:137:static int use_xtal = RADIO_TEA5764_XTAL; drivers/media/radio/Kconfig:412:config RADIO_TEA5764_XTAL $
Note how a Kconfig variable is defined, then promptly cast aside in favour of an explicitly-defined variable. There's a lot of that in the tree.
[edit] Badref CONFIG variables
Unlike simply unused CONFIG variables (which typically aren't doing any harm), "badref" variables represent variables that are being tested in a source or header file somewhere but are not defined in any Kconfig file (they are a bad reference). That's generally considered potentially more serious since it represents code that isn't being processed properly for one reason or another.
Here's the script:
#!/bin/sh
SCAN_DIR=${1-*}
CVARS=$(find ${SCAN_DIR} -name "*.[ch]" | \
grep -v "mach-types.h" | \
xargs ifnames | \
grep "^CONFIG_" | \
cut -d' ' -f1 | \
sed "s/^CONFIG_//" | \
sed "s/_MODULE$//" | \
sort -u)
ALL_KC_FILES=$(find . -name "Kconfig*")
#
# Scan the entire tree, just to see what turns up.
#
for cv in ${CVARS} ; do
egrep -q "^[[:space:]]*config[[:space:]]+${cv}\b" ${ALL_KC_FILES} ||
grep -q "^menuconfig *${cv}$" ${ALL_KC_FILES} ||
egrep -qr "^[[:space:]]*#[[:space:]]*define[[:space:]]+CONFIG_${cv}\b" * || {
echo ">>>>> ${cv}"
grep -rwn "CONFIG_${cv}" * | grep -v defconfig
grep -rwn "${cv}" * | grep -v defconfig
}
done 2> /dev/null
Let's do a sample run and see what's happening under, say, drivers/mtd:
$ find_badref_configs.sh drivers/mtd >>>>> MTD_NAND_OMAP_HWECC drivers/mtd/nand/omap2.c:528:#ifdef CONFIG_MTD_NAND_OMAP_HWECC drivers/mtd/nand/omap2.c:980:#ifdef CONFIG_MTD_NAND_OMAP_HWECC >>>>> MTD_SUPERH_RESERVE drivers/mtd/maps/solutionengine.c:38:#ifdef CONFIG_MTD_SUPERH_RESERVE drivers/mtd/maps/solutionengine.c:44: .size = CONFIG_MTD_SUPERH_RESERVE, drivers/mtd/maps/solutionengine.c:54:#endif /* CONFIG_MTD_SUPERH_RESERVE */ drivers/mtd/maps/solutionengine.c:97:#ifdef CONFIG_MTD_SUPERH_RESERVE drivers/mtd/maps/solutionengine.c:100: CONFIG_MTD_SUPERH_RESERVE); drivers/mtd/maps/solutionengine.c:104:#endif /* CONFIG_MTD_SUPERH_RESERVE */ >>>>> REDWOOD_6 drivers/net/smc91x.h:86:#elif defined(CONFIG_REDWOOD_5) || defined(CONFIG_REDWOOD_6) drivers/mtd/maps/redwood.c:25:#if !defined (CONFIG_REDWOOD_6) drivers/mtd/maps/redwood.c:72:#else /* CONFIG_REDWOOD_6 */ drivers/mtd/maps/redwood.c:111:#endif /* CONFIG_REDWOOD_6 */ arch/powerpc/platforms/40x/Kconfig:82:#config REDWOOD_6 drivers/net/Kconfig:909: depends on ARM || REDWOOD_5 || REDWOOD_6 || M32R || SUPERH || \ drivers/mtd/maps/Kconfig:324: depends on MTD_CFI && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 )
As you can see, there are three badrefs:
- MTD_NAND_OMAP_HWECC
- MTD_SUPERH_RESERVE
- REDWOOD_6
which are listed, each followed by a tree-wide grep for all occurrences of that string just to confirm where they actually show up.
Even core code is not immune to badrefs:
$ find_badref_configs.sh kernel >>>>> SLOW_WORK_PROC fs/fscache/object.c:56:#ifdef CONFIG_SLOW_WORK_PROC fs/fscache/object.c:72:#ifdef CONFIG_SLOW_WORK_PROC fs/fscache/object.c:367:#ifdef CONFIG_SLOW_WORK_PROC fs/fscache/operation.c:503:#ifdef CONFIG_SLOW_WORK_PROC fs/fscache/operation.c:520:#ifdef CONFIG_SLOW_WORK_PROC include/linux/fscache-cache.h:108:#ifdef CONFIG_SLOW_WORK_PROC kernel/slow-work.h:46:#ifdef CONFIG_SLOW_WORK_PROC kernel/slow-work.h:53:#ifdef CONFIG_SLOW_WORK_PROC kernel/slow-work.h:60:#ifdef CONFIG_SLOW_WORK_PROC kernel/slow-work.h:67:#ifdef CONFIG_SLOW_WORK_PROC ... etc etc ...
[edit] Badref CONFIG variables in Makefiles
Here's a script (related to the above) which identifies references to non-existent CONFIG variables only from Makefiles:
#!/bin/sh
DIR=${1-*}
MAKE_CONFIGS=$(grep -- "-\$(CONFIG_" $(find ${DIR} -name 'Makefile*') | \
grep -v "^#" | \
sed -e "s/.*CONFIG_\([A-Za-z0-9_]*\).*/\1/" | \
sort -u)
allkcfiles=$(find . -name "Kconfig*")
for mc in ${MAKE_CONFIGS} ; do
grep -wq ${mc} ${allkcfiles} || {
echo "===== ${mc} ====="
# grep -r ${mc} *
grep -rw "CONFIG_${mc}" $(find . -name Makefile)
grep -w ${mc} ${allkcfiles}
}
done
A sample run (restricting ourselves to the drivers/ directory):
find_badref_make_configs.sh drivers ===== ARCH_QSD ===== ./drivers/staging/dream/camera/Makefile:obj-$(CONFIG_ARCH_QSD) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o ===== ATHEROS_AR71XX ===== ./drivers/net/wireless/ath/ath9k/Makefile:ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o ===== HDPU_FEATURES ===== ./drivers/misc/Makefile:obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ ./drivers/misc/hdpuftrs/Makefile:obj-$(CONFIG_HDPU_FEATURES) := hdpu_cpustate.o hdpu_nexus.o ===== KEYBOARD_QT2160 ===== ./drivers/input/keyboard/Makefile:obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o ===== MSM_ADSP ===== ./drivers/staging/dream/Makefile:obj-$(CONFIG_MSM_ADSP) += qdsp5/ smd/
Whether or not any of this is considered to be serious is a matter of opinion, but it obviously affects whether certain portions of the kernel code get compiled or not.
[edit] Badref "select" directives
Typically not a serious issue, there are a number of Kconfig "select" directives that refer to non-existent Kconfig variables. Here's the script to track them down, by sub-directory if you choose:
#!/bin/sh
DIR=${1-*}
DIRKS=$(find ${DIR} -name Kconfig*)
ALLKS=$(find . -name Kconfig*)
# echo "DIRKS = ${DIRKS}"
# echo "ALLKS = ${ALLKS}"
SELS=$(grep -h "^ select " ${DIRKS} | \
sed -e 's|.*select[ ]*\([^ ]*\).*|\1|' | \
sort -u)
# echo "${SELS}"
for sel in ${SELS} ; do
# echo "Testing ${sel} ..."
grep -q "config[ ]*${sel}$" ${ALLKS} || {
echo "===== ${sel}"
grep -rwn ${sel} *
}
done
A sample run:
find_badref_selects.sh drivers ===== SPI_MASTER_OF drivers/spi/Kconfig:156: select SPI_MASTER_OF ===== USB_GADGET_S3C_HSOTG_PIO drivers/usb/gadget/Kconfig:297: select USB_GADGET_S3C_HSOTG_PIO ===== XEN_XENBUS_FRONTEND drivers/input/Kconfig:168: select XEN_XENBUS_FRONTEND drivers/video/Kconfig:2084: select XEN_XENBUS_FRONTEND
To manually confirm, feel free to do your own tree-wide grep:
$ grep -r XEN_XENBUS * drivers/input/Kconfig: select XEN_XENBUS_FRONTEND drivers/video/Kconfig: select XEN_XENBUS_FRONTEND include/xen/xenbus.h:#ifndef _XEN_XENBUS_H include/xen/xenbus.h:#define _XEN_XENBUS_H include/xen/xenbus.h:#endif /* _XEN_XENBUS_H */
[edit] Finding "badref" linux header files
Here's a quick and dirty script that locates references to non-existent header files under the directory include/linux. The script:
#!/bin/sh
#
# Identify header files under include/linux that are being
# included but simply don't exist.
#
REFS=$(grep -rh "^ *# *include <linux/.*\.h>$" * | \
sed -e 's|.*include <linux/\(.*\)>|\1|' | \
sort -u)
for r in ${REFS} ; do
if [ ! -f include/linux/${r} ] ; then
echo "=== Missing: include/linux/${r} ==="
grep -rn "^#include <linux/${r}>$" *
fi
done
There's no sub-directory argument at the moment -- just run it from the top of the source tree, it doesn't take long.
$ find_badref_linux_headers.sh === Missing: include/linux/android_pmem.h === drivers/staging/dream/pmem.c:23:#include <linux/android_pmem.h> drivers/staging/dream/camera/msm_camera.c:22:#include <linux/android_pmem.h> drivers/staging/dream/camera/msm_vfe7x.c:9:#include <linux/android_pmem.h> drivers/staging/dream/qdsp5/adsp_driver.c:27:#include <linux/android_pmem.h> === Missing: include/linux/earlysuspend.h === drivers/staging/dream/synaptics_i2c_rmi.c:22:#include <linux/earlysuspend.h> === Missing: include/linux/fec.h === arch/arm/mach-mx25/mx25pdk.c:25:#include <linux/fec.h> === Missing: include/linux/gpio_event.h === drivers/staging/dream/gpio_axis.c:18:#include <linux/gpio_event.h> drivers/staging/dream/gpio_event.c:19:#include <linux/gpio_event.h> drivers/staging/dream/gpio_input.c:18:#include <linux/gpio_event.h> drivers/staging/dream/gpio_matrix.c:18:#include <linux/gpio_event.h> drivers/staging/dream/gpio_output.c:18:#include <linux/gpio_event.h> ... etc etc, you get the idea ...

