blob: b265b45e09b56da08966b2cab3633215e3d4f76a [file] [log] [blame]
#!/bin/sh -efu
# Copyright 2011-2012 Intel Corporation
# Author: Artem Bityutskiy
# License: GPLv2
srcdir="$(readlink -ev -- ${0%/*})"
PATH="$srcdir:$srcdir/libshell:$PATH"
. shell-error
. shell-args
. shell-signal
. shell-quote
. aiaiai-sh-functions
PROG="${0##*/}"
# This is a small trick to make sure the script is portable - check if 'dash'
# is present, and if yes - use it.
if can_switch_to_dash; then
exec dash -euf -- "$srcdir/$PROG" "$@"
exit $?
fi
show_usage()
{
cat <<-EOF
Usage: $PROG [options] -- ...
This is an internal Aiaiai helper program which runs various source code
analysis tools when building the kernel. Options for "$PROG" has to go first,
then there has to be a "--" delimiter, and then any number of options which are
not interpreted by this script but passed further to the code analysis tool
(sparse, smatch, etc).
Options:
--sparse check with sparse;
--smatch check with smatch;
--cppcheck check with cppcheck;
--coccinelle=PATH check with coccinelle (spatch) using coccinelle
scripts from PATH; the scripts have to have ".cocci"
extention and may reside anywhere under PATH
--check-only=FILE check only files listed in FILE;
-h, --help show this text and exit.
EOF
}
fail_usage()
{
[ -z "$1" ] || printf "%s\n" "$1"
show_usage
exit 1
}
# Fetch the file name to check from the input arguments.
get_file_to_check()
{
# The file name is the last argument
for file; do true; done
printf "%s" "$file"
}
# Check the file against all the coccinelle scripts we have
run_coccinelle()
{
local spatch spatches flags pid
local pids=
spatches="$(find "$cocci_path" -name '*.cocci')"
if [ -z "$spatches" ]; then
die "no coccinelle scripts (*.cocci) found in \"$cocci_path\""
fi
for spatch in $spatches; do
# Coccinelle is not stable enough so far and dies because of
# internal issues sometimes or just never stops. So we specify
# a timeout as well as ignore its error code.
flags="-D report --no-show-diff --very-quiet --no-includes --include-headers --timeout 60"
# Run coccinelle for each semantic patch in parallel. This may load the
# system too heavily, though. We use aiaiai-locker to make sure
# we have non-scrambled output.
#
# Also redirect stderr to /dev/null since it crashes sometimes
# and prints different kind of uninteresting debugging
# information to stderr
aiaiai-locker -s -l "$tmpdir/lockfile" -c \
"spatch $flags --sp-file $spatch $file_to_check" 2> /dev/null ||: &
pids="$pids $!"
done
for pid in $pids; do
wait "$pid"
done
}
tmpdir=
cleanup_handler()
{
rm -rf $verbose -- "$tmpdir"
# Just in case we were interrupted, kill all our children
local child
for child in $(ps -o pid --no-headers --ppid "$$"); do
kill "$child" > /dev/null 2>&1 ||:
done
}
set_cleanup_handler cleanup_handler
TEMP=`getopt -n $PROG -o h --long sparse,smatch,cppcheck,coccinelle:,check-only:,help -- "$@"` ||
fail_usage ""
eval set -- "$TEMP"
[ "$#" -gt "0" ] || exit 0
run_sparse=
run_smatch=
run_cppcheck=
run_coccinelle=
cocci_path=
check_only=
while true; do
case "$1" in
--sparse)
run_sparse=1
program_required "sparse" "See section 'sparse' in doc/README"
;;
--smatch)
run_smatch=1
program_required "smatch" "See section 'smatch' in doc/README"
;;
--cppcheck)
run_cppcheck=1
program_required "cppcheck" "Usually Linux distribution provide a cppcheck package"
;;
--coccinelle)
run_coccinelle=1
cocci_path="$(opt_check_dir "$1" "$2")"
program_required "spatch" "Usually Linux distribution provide a 'spatch' or 'coccinelle' package"
shift
;;
--check-only)
check_only="$(opt_check_read "$1" "$2")"
shift
;;
-h|--help)
show_usage
exit 0
;;
--) shift; break
;;
*) fail_usage "Unrecognized option: $1"
;;
esac
shift
done
compile_helpers "$srcdir"
program_required "aiaiai-locker" ""
tmpdir="$(mktemp -dt "$PROG.XXXX")"
file_to_check="$(get_file_to_check "$@")"
# Exit immediately if there is nothing to check
if [ -z "$run_sparse" ] && [ -z "$run_smatch" ] && [ -z "$run_cppcheck" ] &&
[ -z "$run_coccinelle" ]; then
exit 0
fi
if [ -n "$check_only" ]; then
match=
files="$(cat "$check_only")"
for file in $files; do
match="$(printf "%s" "$@" | sed -n "/$(quote_sed_regexp "$file")/p")"
[ -z "$match" ] || break
done
[ -n "$match" ] || exit 0
fi
# Run all the tools in background
if [ -n "$run_sparse" ]; then
# Sparse uses stderr for check results, not sure about stdout
sparse -Wsparse-all "$@" > "$tmpdir/sparse" 2>&1 &
pid_sparse="$!"
fi
if [ -n "$run_smatch" ]; then
# Smatch uses stderr for reporting about internal issues and stdout for
# check results
smatch --no-data --project=kernel "$@" > "$tmpdir/smatch" 2>/dev/null &
pid_smatch="$!"
fi
if [ -n "$run_cppcheck" ]; then
# Cppcheck uses stderr for reporting about internal issues and stdout for
# check results
cppcheck -f -q --template=gcc "$file_to_check" > "$tmpdir/cppcheck" 2>/dev/null &
pid_cppcheck="$!"
fi
if [ -n "$run_coccinelle" ]; then
# Coccinelle uses stderr for reporting about internal issues and stdout for
# check results
run_coccinelle > "$tmpdir/coccinelle" 2>/dev/null &
pid_coccinelle="$!"
fi
# Wait for the tools
if [ -n "$run_sparse" ]; then
wait "$pid_sparse" ||:
if [ -s "$tmpdir/sparse" ]; then
cat "$tmpdir/sparse" | sed -e "s/$/ [sparse]/" 1>&2
fi
fi
if [ -n "$run_smatch" ]; then
wait "$pid_smatch" ||:
if [ -s "$tmpdir/smatch" ]; then
cat "$tmpdir/smatch" | sed -e "s/$/ [smatch]/" 1>&2
fi
fi
if [ -n "$run_cppcheck" ]; then
wait "$pid_cppcheck" ||:
if [ -s "$tmpdir/cppcheck" ]; then
cat "$tmpdir/cppcheck" | sed -e "s/$/ [cppcheck]/" 1>&2
fi
fi
if [ -n "$run_coccinelle" ]; then
wait "$pid_coccinelle" ||:
if [ -s "$tmpdir/coccinelle" ]; then
cat "$tmpdir/coccinelle" | sed -e "s/$/ [coccinelle]/" 1>&2
fi
fi