blob: bb04b77e623569d3da70123a284335f99958d7be [file] [log] [blame]
#!/bin/bash
# Linux kernel coccicheck
#
# For more detailed documentation refer to:
#
# https://bottest.wiki.kernel.org/coccicheck
#
# This documentation always refers to the linux-next version of the script.
#
# This script requires at least spatch
# version 1.0.0-rc11.
DIR="$(dirname $(readlink -f $0))/.."
SPATCH="`which ${SPATCH:=spatch}`"
srctree="$(readlink -f $srctree)/"
if [ "$KBUILD_EXTMOD" != "" ] ; then
if [ -d "$KBUILD_EXTMOD" ]; then
KBUILD_EXTMOD="$(readlink -f $KBUILD_EXTMOD)/"
fi
fi
if [ ! -x "$SPATCH" ]; then
echo 'spatch is part of the Coccinelle project and is available at http://coccinelle.lip6.fr/'
exit 1
fi
SPATCH_VERSION=$($SPATCH --version | head -1 | awk '{print $3}')
SPATCH_VERSION_NUM=$(echo $SPATCH_VERSION | ${DIR}/scripts/ld-version.sh)
USE_JOBS="no"
$SPATCH --help | grep "\-\-jobs" > /dev/null && USE_JOBS="yes"
function can_use_glimpse()
{
$SPATCH --help | grep "\-\-use\-glimpse" > /dev/null
if [ $? -ne 0 ]; then
echo "no"
return
fi
if [ ! -f $DIR/.glimpse_index ]; then
echo "no"
return
fi
# As of coccinelle 1.0.5 --use-glimpse cannot be used with M= other
# than the base directory. We expect a future release will let us
# specify the full path of the glimpse index but even if that's
# supported, glimpse use seems to require an index per every
# directory parsed, so you'd have to generate a glimpse index
# per directory. If M=path is used (where epath is not the top level)
# we'll have to fallback to alternatives then.
if [ "$KBUILD_EXTMOD" != "./" -a "$KBUILD_EXTMOD" != "" ]; then
echo "no"
return
fi
echo yes
}
function can_use_idutils()
{
$SPATCH --help | grep "\-\-use\-idutils" > /dev/null
if [ $? -ne 0 ]; then
echo "no"
return
fi
# As of coccinelle 1.0.5 --use-idutils will bust if one uses
# idutils with an index out of the main tree.
if [ "$KBUILD_EXTMOD" != "./" -a "$KBUILD_EXTMOD" != "" ]; then
echo "no"
return
fi
if [ -f $DIR/ID -o -f $DIR/.id-utils.index ]; then
echo "yes"
return
fi
echo "no"
}
function can_use_gitgrep()
{
$SPATCH --help | grep "\-\-use\-gitgrep" > /dev/null
if [ $? -ne 0 ]; then
echo "no"
return
fi
if [ ! -d $DIR/.git ]; then
echo "no"
return
fi
echo "yes"
}
# Indexing USE_* optimizations heuristics.
#
# Linux runs on git (TM). However, if you have supplemental indexing options
# you may use them to help Coccinelle further. If you are using Coccinelle
# within a target git tree --use-gitrep will be used, and this should
# suffice for most uses. If you however want optimal performance do
# consider embracing a supplemental indexing as part of your development.
# For instance glimpse, and idutils can be used, however you should
# be sure to update the indexes as often as you update your git tree to
# ensure your indexes are not stale.
#
# idutils is currently not as efficient as glimpse because the query language
# for glimpse is simpler, so when idutils is used more filtering has to be
# done at the ocaml level within Coccinelle. Glimpse allows queries that are
# arbitrary formulas, up to a limited level of complexity, involving both
# && and ||. For idutils, Coccinelle runs lid intersections on the result.
#
# You can override these heuristics with COCCI_INDEX="--use-gitgrep" for
# example. This will force to use --use-gitgrep even if you have a glimpse
# index. Other examples:
#
# $ export COCCI=scripts/coccinelle/misc/irqf_oneshot.cocci
# $ make coccicheck V=1 MODE=report COCCI_INDEX="--use-coccigrep"
# $ make coccicheck V=1 MODE=report COCCI_INDEX="--use-idutils ID"
# $ make coccicheck V=1 MODE=report COCCI_INDEX="--use-glimpse"
# $ make coccicheck V=1 MODE=report COCCI_INDEX="--use-gitgrep"
#
# The order of heuristics for indexing used by coccicheck is listed below.
#
# 0. Glimpse currently should outperform all indexing options. so if a glimpse
# index is used we use it. Refer to Linux scripts/glimpse.sh for details.
# If you think you should be getting better performance with glimpse than
# what you would expect inspect the stderr log file for cocciecheck:
#
# ${DIR}/coccicheck.$$.err
#
# If glimpse is running correctly there should be very few occurrences
# of "Skipping", also coccinelle will inform you if it could not use
# glimpse. As an example an output of the following would indicate glimpse
# was properly used on ${DIR}/coccicheck.$$.err :
#
# There are matches to 1252 out of 47281 files
# glimpse request = request_threaded_irq
#
# 1. Use idutils next. You'll need to generate an index using either of these:
#
# a) mkid -s
# By default this dumps the index into ./ID
#
# b) mkid -i C --output .id-utils.index *
# This method is provided with coccinelle repo on
# scripts/idutils_index.sh
#
# 2. Next best is --use-gitgrep and if you are working within a git tree
# this will be used by default.
#
# 3. By default coccinelle internally uses --use-coccigrep if no indexing
# options are requested and your version of coccinelle supports it so we
# do not need to be specific about requesting that as a fallback mechanism.
# Use of --use-coccigrep is comparable to --use-gitgrep.
#
# XXX: Glimpse is not well maintained. See if we can add similar indexing
# features and query language glimpse supports to git.
if [ "$COCCI_INDEX" = "" ] ; then
USE_GLIMPSE=$(can_use_glimpse)
USE_IDUTILS=$(can_use_idutils)
USE_GITGREP=$(can_use_gitgrep)
fi
# The verbosity may be set by the environmental parameter V=
# as for example with 'make V=1 coccicheck'
if [ -n "$V" -a "$V" != "0" ]; then
VERBOSE="$V"
else
VERBOSE=0
fi
if [ -z "$J" ]; then
NPROC=$(getconf _NPROCESSORS_ONLN)
else
NPROC="$J"
fi
# You can use SPFLAGS to append extra arguments to coccicheck.
# A good example is if you want to debug your cocci script, you can
# for instance use the following:
#
# $ export COCCI=scripts/coccinelle/misc/irqf_oneshot.cocci
# $ time make coccicheck MODE=report SPFLAGS="--profile --show-trying" M=./drivers/mfd/arizona-irq.c
#
# "--show-trying" should show you what rule is being processed as it goes, if
# you have issues with a rule getting stuck you can then inspect the file:
#
# ${DIR}/coccicheck.$$.err
#
# which will have the profile output.
#
# --profile will not output if --very-quiet is used, so avoid it.
if [ "$SPFLAGS" == *"--profile"* -o "$SPFLAGS" == "--show-trying" ]; then
FLAGS="--quiet $SPFLAGS"
else
FLAGS="--very-quiet $SPFLAGS"
fi
# spatch only allows include directories with the syntax "-I include"
# while gcc also allows "-Iinclude" and "-include include"
COCCIINCLUDE=${LINUXINCLUDE//-I/-I }
COCCIINCLUDE=${COCCIINCLUDE// -include/ --include}
if [ "$C" = "1" -o "$C" = "2" ]; then
ONLINE=1
# Take only the last argument, which is the C file to test
shift $(( $# - 1 ))
OPTIONS="$COCCIINCLUDE $1"
else
ONLINE=0
if [ "$KBUILD_EXTMOD" = "" ] ; then
OPTIONS="--dir $srctree $COCCIINCLUDE"
else
OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE"
fi
fi
if [ "$KBUILD_EXTMOD" != "" ] ; then
OPTIONS="--patch $srctree $OPTIONS"
fi
if [ "$MODE" = "" ] ; then
if [ "$ONLINE" = "0" ] ; then
echo 'You have not explicitly specified the mode to use. Using default "report" mode.'
echo 'Available modes are the following: patch, report, context, org'
echo 'You can specify the mode with "make coccicheck MODE=<mode>"'
echo 'Note however that some modes are not implemented by some semantic patches.'
fi
MODE="report"
fi
if [ "$MODE" = "chain" ] ; then
if [ "$ONLINE" = "0" ] ; then
echo 'You have selected the "chain" mode.'
echo 'All available modes will be tried (in that order): patch, report, context, org'
fi
elif [ "$MODE" = "report" -o "$MODE" = "org" ] ; then
FLAGS="$FLAGS --no-show-diff"
fi
if [ "$ONLINE" = "0" ] ; then
echo ''
echo 'Please check for false positives in the output before submitting a patch.'
echo 'When using "patch" mode, carefully review the patch before submitting it.'
echo ''
fi
if [ "$USE_JOBS" = "no" ]; then
trap kill_running SIGTERM SIGINT
declare -a SPATCH_PID
else
OPTIONS="$OPTIONS --jobs $NPROC --chunksize 1"
fi
# Check COCCI_INDEX first to manual override, otherwise rely on
# internal heuristics documented above.
if [ "$COCCI_INDEX" != "" ] ; then
OPTIONS="$OPTIONS $COCCI_INDEX"
elif [ "$USE_GLIMPSE" = "yes" ]; then
OPTIONS="$OPTIONS --use-glimpse"
elif [ "$USE_IDUTILS" = "yes" ]; then
index=""
if [ -f $DIR/ID ]; then
index="$DIR/ID"
elif [ -f $DIR/.id-utils.index ]; then
index="$DIR/.id-utils.index"
else
echo "idutils index not found, expected: $DIR/ID or $DIR/.id-utils.index"
exit 1
fi
OPTIONS="$OPTIONS --use-idutils $index"
elif [ "$USE_GITGREP" = "yes" ]; then
OPTIONS="$OPTIONS --use-gitgrep"
fi
run_cmd_parmap() {
if [ $VERBOSE -ne 0 ] ; then
echo "Running ($NPROC in parallel): $@"
fi
$@ 2>&1
if [[ $? -ne 0 ]]; then
echo "coccicheck failed"
exit $?
fi
}
run_cmd_old() {
local i
if [ $VERBOSE -ne 0 ] ; then
echo "Running ($NPROC in parallel): $@"
fi
for i in $(seq 0 $(( NPROC - 1)) ); do
eval "$@ --max $NPROC --index $i &"
SPATCH_PID[$i]=$!
if [ $VERBOSE -eq 2 ] ; then
echo "${SPATCH_PID[$i]} running"
fi
done
wait
}
run_cmd() {
if [ "$USE_JOBS" = "yes" ]; then
run_cmd_parmap $@
else
run_cmd_old $@
fi
}
kill_running() {
for i in $(seq 0 $(( NPROC - 1 )) ); do
if [ $VERBOSE -eq 2 ] ; then
echo "Killing ${SPATCH_PID[$i]}"
fi
kill ${SPATCH_PID[$i]} 2>/dev/null
done
}
coccinelle () {
COCCI="$1"
OPT=`grep "Option" $COCCI | cut -d':' -f2`
REQ=`grep "Requires" $COCCI | cut -d':' -f2 | sed "s| ||"`
REQ_NUM=$(echo $REQ | ${DIR}/scripts/ld-version.sh)
if [ "$REQ_NUM" != "0" ] ; then
if [ "$SPATCH_VERSION_NUM" -lt "$REQ_NUM" ] ; then
echo "Skipping coccinele SmPL patch: $COCCI"
echo "You have coccinelle: $SPATCH_VERSION"
echo "This SmPL patch requires: $REQ"
return
fi
fi
# The option '--parse-cocci' can be used to syntactically check the SmPL files.
#
# $SPATCH -D $MODE $FLAGS -parse_cocci $COCCI $OPT > /dev/null
if [ $VERBOSE -ne 0 -a $ONLINE -eq 0 ] ; then
FILE=`echo $COCCI | sed "s|$srctree/||"`
echo "Processing `basename $COCCI`"
echo "with option(s) \"$OPT\""
echo ''
echo 'Message example to submit a patch:'
sed -ne 's|^///||p' $COCCI
if [ "$MODE" = "patch" ] ; then
echo ' The semantic patch that makes this change is available'
elif [ "$MODE" = "report" ] ; then
echo ' The semantic patch that makes this report is available'
elif [ "$MODE" = "context" ] ; then
echo ' The semantic patch that spots this code is available'
elif [ "$MODE" = "org" ] ; then
echo ' The semantic patch that makes this Org report is available'
else
echo ' The semantic patch that makes this output is available'
fi
echo " in $FILE."
echo ''
echo ' More information about semantic patching is available at'
echo ' http://coccinelle.lip6.fr/'
echo ''
if [ "`sed -ne 's|^//#||p' $COCCI`" ] ; then
echo 'Semantic patch information:'
sed -ne 's|^//#||p' $COCCI
echo ''
fi
fi
if [ "$MODE" = "chain" ] ; then
run_cmd $SPATCH -D patch \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS || \
run_cmd $SPATCH -D report \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || \
run_cmd $SPATCH -D context \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS || \
run_cmd $SPATCH -D org \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || exit 1
elif [ "$MODE" = "rep+ctxt" ] ; then
run_cmd $SPATCH -D report \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff && \
run_cmd $SPATCH -D context \
$FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1
else
run_cmd $SPATCH -D $MODE $FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1
fi
}
if [ "$COCCI" = "" ] ; then
for f in `find $srctree/scripts/coccinelle/ -name '*.cocci' -type f | sort`; do
coccinelle $f
done
else
coccinelle $COCCI
fi