Kernel cleanup scripts

From Crashcourse Wiki

Jump to: navigation, search

Here are some scripts I wrote quite some time ago to scan the Linux kernel source tree (or user-selected parts of it) and identify inconsistencies and possible errors in everything related to the Kbuild infrastructure, including (as you can see):

  • unused config variables (variables defined in a Kconfig file but never tested by anyone),
  • bad references to config variables (conversely, variables not defined in a Kconfig that are tested somewhere)

and so on -- the scripts should be self-explanatory.

In most cases, you run the script from the top of the kernel 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 them in Python, and use a multi-level associative array to build a tree of the config variables to support more interesting scanning.

In any event, check the output carefully since, sometimes, there are perfectly innocent reasons for what look like oddities; there's always a possibility for false positives based on scenarios I didn't consider, or just plain errors in my scripts.

Feedback/comments/suggestions to

Contents (updated Sep 18, 2014)

This script locates CONFIG_* variables that are defined in some Kconfig* file within the part of the source tree of interest, but appear to be entirely unused and untested anywhere in the entire 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.


# Uncomment whatever debugging you want to see.


#  Collect config variables from Kconfig* files of interest.

SELECTED_KCONFIG_FILES=$(find ${SCAN_DIR} -name "Kconfig*")

if [ "${SELECTED_KCONFIG_FILES}" = "" ] ; then
        echo "No kconfig files, exiting."
        exit 1

ALL_KCONFIG_FILES=$(find * -name "Kconfig*")


KCONFIG_VARS1=$(grep ^config ${SELECTED_KCONFIG_FILES} | awk '{print $2}' | sort -u)
KCONFIG_VARS2=$(grep ^menuconfig ${SELECTED_KCONFIG_FILES} | awk '{print $2}' | sort -u)


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


Here's a sample run, examining the sub-directory drivers/net/wireless, which identifies a number of variables that still exist in Kconfig files that no one appears to be checking:

$ drivers/net/wireless
>>>>> B43_BCMA_PIO
drivers/net/wireless/b43/Kconfig:113:config B43_BCMA_PIO
>>>>> B43_BUSES_SSB
drivers/net/wireless/b43/Kconfig:52:config B43_BUSES_SSB
drivers/net/wireless/b43legacy/Kconfig:85:config B43LEGACY_DMA_MODE
drivers/net/wireless/b43legacy/Kconfig:31:config B43LEGACY_PCICORE_AUTOSELECT
drivers/net/wireless/b43legacy/Kconfig:92:config B43LEGACY_PIO_MODE
drivers/net/wireless/b43/Kconfig:68:config B43_PCICORE_AUTOSELECT
>>>>> B43_PIO
drivers/net/wireless/b43/Kconfig:119:config B43_PIO

Again, as you can see, a lot of that looks like legacy content that was probably unintentionally left in the Kconfig files after the underlying code was deleted, so it's rarely a real concern. But you never know. (updated Sep 18, 2014)

Unlike simply unused CONFIG variables (which typically aren't doing any harm), what I call "badref" config variables represent variables that are being tested in a source or header file somewhere but are not defined in any Kconfig file (they represent a "bad reference" to a config variable). That's generally considered more serious since it represents code that potentially isn't being processed properly for one reason or another.

Here's the script:



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_KCONFIG_FILES=$(find . -name "Kconfig*")

#  Scan the entire tree, just to see what turns up.

for cv in ${CVARS} ; do
        # echo "Testing ${cv} ..."
        egrep -q "^[[:space:]]*config[[:space:]]+${cv}\b" ${ALL_KCONFIG_FILES} ||
        grep -q "^menuconfig *${cv}$" ${ALL_KCONFIG_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/net/wireless:

$ drivers/net/wireless
>>>>> ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/base.c:102:#ifdef CONFIG_ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/base.c:145:#ifdef CONFIG_ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/led.c:165:#ifndef CONFIG_ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/led.c:174:#ifdef CONFIG_ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/ath5k.h:1650:#ifdef CONFIG_ATHEROS_AR231X
drivers/net/wireless/ath/ath5k/Kconfig:3:	depends on (PCI || ATHEROS_AR231X) && MAC80211
drivers/net/wireless/ath/ath5k/Kconfig:9:	select ATH5K_AHB if (ATHEROS_AR231X && !PCI)
drivers/net/wireless/ath/ath5k/Kconfig:10:	select ATH5K_PCI if (!ATHEROS_AR231X && PCI)
drivers/net/wireless/ath/ath5k/Kconfig:57:	depends on (ATHEROS_AR231X && !PCI)
drivers/net/wireless/ath/ath5k/Kconfig:64:	depends on (!ATHEROS_AR231X && PCI)

So, apparently, just the one. And under drivers/staging:

>>>>> B4860G100
drivers/staging/gs_fpgaboot/io.c:34:#ifdef CONFIG_B4860G100
drivers/staging/gs_fpgaboot/io.c:36:#endif /* CONFIG_B4860G100 */
drivers/staging/gs_fpgaboot/io.c:95:#ifdef CONFIG_B4860G100
drivers/staging/gs_fpgaboot/io.c:300:#endif /* CONFIG_B4860G100 */
drivers/staging/rtl8192u/r8192U_core.c:27:#ifndef CONFIG_FORCE_HARD_FLOAT
drivers/staging/emxx_udc/emxx_udc.h:440:#ifdef CONFIG_MACH_EMGR
$ (updated Sep 17, 2014)

Here's a script (related to the above) which identifies references to non-existent CONFIG variables only from Makefiles:



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}

Here's the output from testing the entire source tree:

>>>>> ARCH_HIP04
./drivers/clk/hisilicon/Makefile:obj-$(CONFIG_ARCH_HIP04)	+= clk-hip04.o
./drivers/clk/Makefile:obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
>>>>> IPIPE
./arch/blackfin/kernel/Makefile:obj-$(CONFIG_IPIPE)                  += ipipe.o
./arch/metag/kernel/Makefile:obj-$(CONFIG_METAG_SUSPEND_MEM)		+= suspend.o
./firmware/Makefile:fw-shipped-$(CONFIG_MYRI_SBUS) += myricom/lanai.bin
./drivers/nfc/Makefile:ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
./arch/mn10300/kernel/Makefile:obj-$(CONFIG_PROFILE) += profile.o profile-low.o
./firmware/Makefile:fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
>>>>> X86_X32_ABI
./arch/x86/Makefile:                CONFIG_X86_X32_ABI := y
./arch/x86/Makefile:export CONFIG_X86_X32_ABI
./arch/x86/vdso/Makefile:VDSOX32-$(CONFIG_X86_X32_ABI)	:= y

There used to be a lot more of that, so either a good deal of cleaning has happened in the last year or two, or my script just isn't catching some of it for whatever reason. (updated Sep 18, 2014)

Typically not a serious issue, there are a few Kconfig "select" directives that refer to non-existent Kconfig variables. Here's the script to track them down, by sub-directory if you choose:



DIRKS=$(find ${DIR} -name "Kconfig*")
ALLKS=$(find . -name "Kconfig*")

# echo "DIRKS = ${DIRKS}"
# echo "ALLKS = ${ALLKS}"

SELS=$(grep -h "^[[:space:]*]select[[:space:]]" ${DIRKS} | \
        sed -e 's|^[[:space:]]*select[[:space:]]*\([^[:space:]]*\).*|\1|' |   \
        sort -u)

# echo "${SELS}"

for sel in ${SELS} ; do
        # echo "Testing ${sel} ..."
        grep -q "config[[:space:]]*${sel}$" ${ALLKS} || {
                echo ">>>>> ${sel}"
                grep -rwn ${sel} *

A scan of the entire tree shows only a small number of these left from what used to be a lot of them:

arch/arm/mach-shmobile/Kconfig:15:	select ARCH_HAS_OPP
arch/arm/mach-omap2/Kconfig:28:	select ARCH_HAS_OPP
arch/arm/mach-omap2/Kconfig:47:	select ARCH_HAS_OPP
arch/arm/mach-omap2/Kconfig:59:	select ARCH_HAS_OPP
arch/arm/mach-omap2/Kconfig:66:	select ARCH_HAS_OPP
arch/arm/mach-omap2/Kconfig:75:	select ARCH_HAS_OPP
drivers/devfreq/Kconfig:83:	select ARCH_HAS_OPP
>>>>> ARM_ERRATA_753970
arch/arm/mach-mvebu/Kconfig:40:	select ARM_ERRATA_753970
arch/arm/mach-mvebu/Kconfig:55:	select ARM_ERRATA_753970
arch/score/Kconfig:25:	select GENERIC_HAS_IOMAP
arch/score/Kconfig:30:	select GENERIC_HAS_IOMAP
arch/score/Kconfig:35:	select GENERIC_HAS_IOMAP
arch/powerpc/platforms/44x/Kconfig:217:	select IBM_EMAC_RGMII_WOL
arch/powerpc/platforms/44x/Kconfig:223:	select MMC_SDHCI_OF_476GTR

I'm surprised at how few of these are left, unless I've messed up the script in some way.

Personal tools