Blame test/integration/kpatch-test

Packit Service ac8aad
#!/bin/bash
Packit Service ac8aad
#
Packit Service ac8aad
# kpatch integration test framework
Packit Service ac8aad
#
Packit Service ac8aad
# Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
Packit Service ac8aad
#
Packit Service ac8aad
# This program is free software; you can redistribute it and/or
Packit Service ac8aad
# modify it under the terms of the GNU General Public License
Packit Service ac8aad
# as published by the Free Software Foundation; either version 2
Packit Service ac8aad
# of the License, or (at your option) any later version.
Packit Service ac8aad
#
Packit Service ac8aad
# This program is distributed in the hope that it will be useful,
Packit Service ac8aad
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service ac8aad
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service ac8aad
# GNU General Public License for more details.
Packit Service ac8aad
#
Packit Service ac8aad
# You should have received a copy of the GNU General Public License
Packit Service ac8aad
# along with this program; if not, write to the Free Software
Packit Service ac8aad
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
Packit Service ac8aad
# 02110-1301, USA.
Packit Service ac8aad
#
Packit Service ac8aad
#
Packit Service ac8aad
# This is a basic integration test framework for kpatch, which tests building,
Packit Service ac8aad
# loading, and unloading patches, as well as any other related custom tests.
Packit Service ac8aad
#
Packit Service ac8aad
# This script looks for test input files in the current directory.  It expects
Packit Service ac8aad
# certain file naming conventions:
Packit Service ac8aad
#
Packit Service ac8aad
# - foo.patch: patch that should build successfully
Packit Service ac8aad
#
Packit Service ac8aad
# - bar-FAIL.patch: patch that should fail to build
Packit Service ac8aad
#
Packit Service ac8aad
# - foo-LOADED.test: executable which tests whether the foo.patch module is
Packit Service ac8aad
#   loaded.  It will be used to test that loading/unloading the patch module
Packit Service ac8aad
#   works as expected.
Packit Service ac8aad
#
Packit Service ac8aad
# Any other *.test files will be executed after all the patch modules have been
Packit Service ac8aad
# built from the *.patch files.  They can be used for more custom tests above
Packit Service ac8aad
# and beyond the simple loading and unloading tests.
Packit Service ac8aad
Packit Service ac8aad
shopt -s nullglob
Packit Service ac8aad
Packit Service da4517
# shellcheck disable=SC2046
Packit Service da4517
SCRIPTDIR=$(readlink -f $(dirname $(type -p "$0")))
Packit Service da4517
ROOTDIR=$(readlink -f "$SCRIPTDIR/../..")
Packit Service ac8aad
KPATCH="sudo $ROOTDIR/kpatch/kpatch"
Packit Service ac8aad
unset CCACHE_HASHDIR
Packit Service ac8aad
KPATCHBUILD="$ROOTDIR"/kpatch-build/kpatch-build
Packit Service ac8aad
ERROR=0
Packit Service ac8aad
LOG=test.log
Packit Service da4517
DYNDEBUG_CONTROL=/sys/kernel/debug/dynamic_debug/control
Packit Service da4517
DYNDEBUG_ENABLED=1
Packit Service da4517
rm -f ./*.log
Packit Service ac8aad
Packit Service ac8aad
PATCHDIR="${PATCHDIR:-$PWD}"
Packit Service ac8aad
declare -a PATCH_LIST
Packit Service ac8aad
declare -a TEST_LIST
Packit Service ac8aad
Packit Service ac8aad
usage() {
Packit Service ac8aad
	echo "usage: $0 [options] [patch1 ... patchN]" >&2
Packit Service ac8aad
	echo "		patchN  	Pathnames of patches to test" >&2
Packit Service ac8aad
	echo "		-h, --help	Show this help message" >&2
Packit Service ac8aad
	echo "		-c, --cached	Don't rebuild patch modules" >&2
Packit Service ac8aad
	echo "		-d, --directory	Patch directory" >&2
Packit Service da4517
	echo "		-q, --quick	Test combined patch and -FAIL patches only" >&2
Packit Service da4517
	echo "		--system-kpatch-tools	Use kpatch tools installed in the system" >&2
Packit Service da4517
	echo "		--kpatch-build-opts	Additional options to pass to kpatch-build" >&2
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service da4517
options=$(getopt -o hcd:q -l "help,cached,directory,quick,system-kpatch-tools,kpatch-build-opts:" -- "$@") || exit 1
Packit Service ac8aad
Packit Service ac8aad
eval set -- "$options"
Packit Service ac8aad
Packit Service ac8aad
while [[ $# -gt 0 ]]; do
Packit Service ac8aad
	case "$1" in
Packit Service ac8aad
	-h|--help)
Packit Service ac8aad
		usage
Packit Service ac8aad
		exit 0
Packit Service ac8aad
		;;
Packit Service ac8aad
	-c|--cached)
Packit Service ac8aad
		SKIPBUILD=1
Packit Service ac8aad
		;;
Packit Service ac8aad
	-d|--directory)
Packit Service ac8aad
		PATCHDIR="$2"
Packit Service ac8aad
		shift
Packit Service ac8aad
		;;
Packit Service ac8aad
	-q|--quick)
Packit Service ac8aad
		QUICK=1
Packit Service ac8aad
		;;
Packit Service da4517
	--system-kpatch-tools)
Packit Service da4517
		KPATCH="sudo kpatch"
Packit Service da4517
		KPATCHBUILD="kpatch-build"
Packit Service da4517
		;;
Packit Service da4517
	--kpatch-build-opts)
Packit Service da4517
		KPATCHBUILD_OPTS=$2
Packit Service da4517
		shift
Packit Service da4517
		;;
Packit Service ac8aad
	*)
Packit Service ac8aad
		[[ "$1" = "--" ]] && shift && continue
Packit Service ac8aad
		PATCH_LIST+=("$1")
Packit Service ac8aad
		;;
Packit Service ac8aad
	esac
Packit Service ac8aad
	shift
Packit Service ac8aad
done
Packit Service ac8aad
Packit Service ac8aad
if [[ ${#PATCH_LIST[@]} = 0 ]]; then
Packit Service da4517
	PATCH_LIST=("$PATCHDIR"/*.patch)
Packit Service da4517
	TEST_LIST=("$PATCHDIR"/*.test)
Packit Service ac8aad
	if [[ ${#PATCH_LIST[@]} = 0 ]]; then
Packit Service ac8aad
		echo "No patches found!"
Packit Service ac8aad
		exit 1
Packit Service ac8aad
	fi
Packit Service ac8aad
else
Packit Service ac8aad
	for file in "${PATCH_LIST[@]}"; do
Packit Service ac8aad
		prefix=${file%%.patch}
Packit Service ac8aad
		[[ -e "$prefix-FAIL.test" ]]   && TEST_LIST+=("$prefix-FAIL.test")
Packit Service ac8aad
		[[ -e "$prefix-LOADED.test" ]] && TEST_LIST+=("$prefix-LOADED.test")
Packit Service ac8aad
	done
Packit Service ac8aad
fi
Packit Service ac8aad
Packit Service ac8aad
error() {
Packit Service da4517
	echo "ERROR: $*" |tee -a $LOG >&2
Packit Service ac8aad
	ERROR=$((ERROR + 1))
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
log() {
Packit Service ac8aad
	echo "$@" |tee -a $LOG
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
unload_all() {
Packit Service da4517
	$KPATCH unload --all
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
build_module() {
Packit Service ac8aad
	file=$1
Packit Service da4517
	prefix=$(basename "${file%%.patch}")
Packit Service ac8aad
	modname="test-$prefix"
Packit Service ac8aad
	module="${modname}.ko"
Packit Service ac8aad
Packit Service ac8aad
	if [[ $prefix =~ -FAIL ]]; then
Packit Service ac8aad
		shouldfail=1
Packit Service ac8aad
	else
Packit Service ac8aad
		shouldfail=0
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	if [[ $SKIPBUILD -eq 1 ]]; then
Packit Service ac8aad
		skip=0
Packit Service ac8aad
		[[ $shouldfail -eq 1 ]] && skip=1
Packit Service ac8aad
		[[ -e $module ]] && skip=1
Packit Service ac8aad
		[[ $skip -eq 1 ]] && log "skipping build: $prefix" && return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	log "build: $prefix"
Packit Service ac8aad
Packit Service da4517
	# shellcheck disable=SC2086
Packit Service da4517
	# KPATCHBUILD_OPTS may contain several space-separated options,
Packit Service da4517
	# it should remain without quotes.
Packit Service da4517
	if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n "$modname" "$file" >> $LOG 2>&1; then
Packit Service da4517
		if [[ $shouldfail -eq 0 ]]; then
Packit Service da4517
			error "$prefix: build failed"
Packit Service da4517
			cp "$HOME/.kpatch/build.log" "$prefix.log"
Packit Service da4517
		fi
Packit Service ac8aad
	else
Packit Service ac8aad
		[[ $shouldfail -eq 1 ]] && error "$prefix: build succeeded when it should have failed"
Packit Service ac8aad
	fi
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
run_load_test() {
Packit Service ac8aad
	file=$1
Packit Service da4517
	prefix=$(basename "${file%%.patch}")
Packit Service ac8aad
	modname="test-$prefix"
Packit Service ac8aad
	module="${modname}.ko"
Packit Service da4517
	testprog=$(dirname "$1")/"$prefix-LOADED.test"
Packit Service ac8aad
Packit Service ac8aad
	[[ $prefix =~ -FAIL ]] && return
Packit Service ac8aad
Packit Service ac8aad
	if [[ ! -e $module ]]; then
Packit Service ac8aad
		log "can't find $module, skipping"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	if [[ -e $testprog ]]; then
Packit Service ac8aad
		log "load test: $prefix"
Packit Service ac8aad
	else
Packit Service ac8aad
		log "load test: $prefix (no test prog)"
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
Packit Service ac8aad
	if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: $testprog succeeded before kpatch load"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service da4517
	if ! $KPATCH load "$module" >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: kpatch load failed"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	if [[ -e $testprog ]] && ! $testprog >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: $testprog failed after kpatch load"
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service da4517
	if ! $KPATCH unload "$module" >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: kpatch unload failed"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: $testprog succeeded after kpatch unload"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
run_custom_test() {
Packit Service ac8aad
	testprog=$1
Packit Service da4517
	prefix=$(basename "${testprog%%.test}")
Packit Service ac8aad
Packit Service ac8aad
	[[ $testprog = *-LOADED.test ]] && return
Packit Service ac8aad
Packit Service ac8aad
	log "custom test: $prefix"
Packit Service ac8aad
Packit Service ac8aad
	if ! $testprog >> $LOG 2>&1; then
Packit Service ac8aad
		error "$prefix: test failed"
Packit Service ac8aad
	fi
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
build_combined_module() {
Packit Service ac8aad
Packit Service ac8aad
	if [[ $SKIPBUILD -eq 1 ]] && [[ -e test-COMBINED.ko ]]; then
Packit Service ac8aad
		log "skipping build: combined"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	declare -a COMBINED_LIST
Packit Service ac8aad
	for file in "${PATCH_LIST[@]}"; do
Packit Service ac8aad
		[[ $file =~ -FAIL ]] && log "combine: skipping $file" && continue
Packit Service da4517
		COMBINED_LIST+=("$file")
Packit Service ac8aad
	done
Packit Service ac8aad
	if [[ ${#COMBINED_LIST[@]} -le 1 ]]; then
Packit Service ac8aad
		log "skipping build: combined (only ${#PATCH_LIST[@]} patch(es))"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	log "build: combined module"
Packit Service ac8aad
Packit Service da4517
	# shellcheck disable=SC2086
Packit Service da4517
	if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n test-COMBINED "${COMBINED_LIST[@]}" >> $LOG 2>&1; then
Packit Service ac8aad
		error "combined build failed"
Packit Service da4517
		cp "$HOME/.kpatch/build.log" combined.log
Packit Service ac8aad
	fi
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
run_combined_test() {
Packit Service ac8aad
	if [[ ! -e test-COMBINED.ko ]]; then
Packit Service ac8aad
		log "can't find test-COMBINED.ko, skipping"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	log "load test: combined module"
Packit Service ac8aad
Packit Service ac8aad
	unload_all
Packit Service ac8aad
Packit Service ac8aad
	for testprog in "${TEST_LIST[@]}"; do
Packit Service ac8aad
		[[ $testprog != *-LOADED.test ]] && continue
Packit Service ac8aad
		if $testprog >> $LOG 2>&1; then
Packit Service ac8aad
			error "combined: $testprog succeeded before kpatch load"
Packit Service ac8aad
			return
Packit Service ac8aad
		fi
Packit Service ac8aad
	done
Packit Service ac8aad
Packit Service ac8aad
	if ! $KPATCH load test-COMBINED.ko >> $LOG 2>&1; then
Packit Service ac8aad
		error "combined: kpatch load failed"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	for testprog in "${TEST_LIST[@]}"; do
Packit Service ac8aad
		[[ $testprog != *-LOADED.test ]] && continue
Packit Service da4517
		[ -e "${testprog/-LOADED.test/.patch.disabled}" ] && continue
Packit Service ac8aad
		if ! $testprog >> $LOG 2>&1; then
Packit Service ac8aad
			error "combined: $testprog failed after kpatch load"
Packit Service ac8aad
		fi
Packit Service ac8aad
	done
Packit Service ac8aad
Packit Service ac8aad
	if ! $KPATCH unload test-COMBINED.ko >> $LOG 2>&1; then
Packit Service ac8aad
		error "combined: kpatch unload failed"
Packit Service ac8aad
		return
Packit Service ac8aad
	fi
Packit Service ac8aad
Packit Service ac8aad
	for testprog in "${TEST_LIST[@]}"; do
Packit Service ac8aad
		[[ $testprog != *-LOADED.test ]] && continue
Packit Service ac8aad
		if $testprog >> $LOG 2>&1; then
Packit Service ac8aad
			error "combined: $testprog succeeded after kpatch unload"
Packit Service ac8aad
			return
Packit Service ac8aad
		fi
Packit Service ac8aad
	done
Packit Service ac8aad
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service da4517
# save existing dmesg so we can detect new content
Packit Service da4517
save_dmesg() {
Packit Service da4517
	SAVED_DMESG="$(dmesg | tail -n1)"
Packit Service da4517
}
Packit Service ac8aad
Packit Service da4517
# new dmesg entries since our saved entry
Packit Service da4517
new_dmesg() {
Packit Service da4517
	if ! dmesg | awk -v last="$SAVED_DMESG" 'p; $0 == last{p=1} END {exit !p}'; then
Packit Service da4517
		error "dmesg overflow, try increasing kernel log buffer size"
Packit Service da4517
	fi
Packit Service da4517
}
Packit Service da4517
Packit Service da4517
# shellcheck disable=SC1091
Packit Service da4517
source /etc/os-release
Packit Service da4517
if [[ "${ID}" == "rhel" && "${VERSION_ID%%.*}" == "7" && "${VERSION_ID##*.}" -le "6" ]]; then
Packit Service da4517
	DYNDEBUG_ENABLED=0
Packit Service da4517
	echo "Dynamic debug is not supported on '${PRETTY_NAME}', disabling."
Packit Service ac8aad
fi
Packit Service ac8aad
Packit Service da4517
for file in "${PATCH_LIST[@]}"; do
Packit Service da4517
	if [[ $QUICK != 1 || "$file" =~ -FAIL ]]; then
Packit Service da4517
		build_module "$file"
Packit Service da4517
	fi
Packit Service da4517
done
Packit Service da4517
Packit Service ac8aad
build_combined_module
Packit Service ac8aad
Packit Service ac8aad
unload_all
Packit Service ac8aad
Packit Service da4517
save_dmesg
Packit Service da4517
Packit Service da4517
if [ "${DYNDEBUG_ENABLED}" == "1" ]; then
Packit Service da4517
	prev_dyndebug=$(grep klp_try_switch_task "${DYNDEBUG_CONTROL}" | awk '{print $3;}')
Packit Service da4517
	echo "func klp_try_switch_task +p" >"${DYNDEBUG_CONTROL}" 2>/dev/null
Packit Service da4517
fi
Packit Service da4517
Packit Service ac8aad
if [[ $QUICK != 1 ]]; then
Packit Service ac8aad
	for file in "${PATCH_LIST[@]}"; do
Packit Service da4517
		run_load_test "$file"
Packit Service ac8aad
	done
Packit Service ac8aad
fi
Packit Service ac8aad
Packit Service ac8aad
run_combined_test
Packit Service ac8aad
Packit Service ac8aad
if [[ $QUICK != 1 ]]; then
Packit Service ac8aad
	for testprog in "${TEST_LIST[@]}"; do
Packit Service da4517
		if [[ ! $testprog =~ -FAIL ]]; then
Packit Service da4517
			unload_all
Packit Service da4517
			run_custom_test "$testprog"
Packit Service da4517
		fi
Packit Service ac8aad
	done
Packit Service ac8aad
fi
Packit Service ac8aad
Packit Service ac8aad
Packit Service ac8aad
unload_all
Packit Service ac8aad
Packit Service da4517
if [ "${DYNDEBUG_ENABLED}" == "1" ]; then
Packit Service da4517
	echo "func klp_try_switch_task ${prev_dyndebug}" >"${DYNDEBUG_CONTROL}" 2>/dev/null
Packit Service da4517
fi
Packit Service da4517
Packit Service da4517
if new_dmesg | grep -q "Call Trace"; then
Packit Service da4517
	new_dmesg > dmesg.log
Packit Service da4517
	error "kernel error detected in printk buffer"
Packit Service da4517
fi
Packit Service ac8aad
Packit Service ac8aad
if [[ $ERROR -gt 0 ]]; then
Packit Service ac8aad
	log "$ERROR errors encountered"
Packit Service ac8aad
	echo "see test.log for more information"
Packit Service ac8aad
else
Packit Service ac8aad
	log "SUCCESS"
Packit Service ac8aad
fi
Packit Service ac8aad
Packit Service ac8aad
exit $ERROR