Sophie

Sophie

distrib > Fedora > 13 > x86_64 > by-pkgid > c603c28e3d919f13fb6ff84d3776c516 > files > 1

kcbench-0.3-6.fc13.src.rpm

#!/bin/bash
#
# kcbench - kernel compile benchmark
# Copyright (c) 2007 Thorsten Leemhuis <fedora@leemhuis.info>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

# this is me
myprog_name=kcbench
myprog_version=0.3.3

# set some defaults -- called before cmdoptions are parsed
kcbench_init ()
{
	# verbose?
	verboselevel=2

	# results are normally stable, thus 3 runs after filling the cache should normally be enough
	default_number_of_iterations=3

	# used together with make -j 
	# number of CPUs * 2
	default_number_of_jobs=$(($(grep '^processor' < /proc/cpuinfo  | wc -l)*2))

	# we search for kernels to compile here
	# note that I didn't use /usr/src/ on purpose as localtion -- kernels there 
	# might be modified
	default_sources_dir="/usr/share/kcbench-data/"

	# retrieve settings from file
	if [[ -e "${HOME}/.kcbench" ]]; then
		source "${HOME}/.kcbench"
	fi
}


# check everthing before starting
kcbench_startupchecks()
{
	# check for tools we need
	for tool in make gcc ld awk /usr/bin/time; do
		if ! type ${tool} &> /dev/null ; then
			echo "Could not find ${tool}"
			exit 2
		fi
	done

	# check if cron or other stuff runs and give a warning
	if [[ ! "${ignore_running_apps}" ]]; then
		local runningtasks="$(echo $(ps -A | grep --word -e crond -e httpd -e atd -e sendmail -e smbd | awk '{print $4}' | sort | uniq))"
		if [[ "${runningtasks}" ]] ; then
			echo "WARNING: There are some daemons runnning in the background:" >&2
			echo "  ${runningtasks}" >&2
			echo "  Those might disturb the benchmark; you should disable them!" >&2
			echo "  (use '--ignore-running-apps' to disabled this warning" >&2
			sleep 5
		fi
	fi

	# find a srctree to compile!
	if [[ "${compile_srctree}" ]]; then
		# user provided some informations what kernel to use
		if [[ -e "${compile_srctree}/include/linux/kernel.h" ]] ; then
			# is a local tree -- but we need the full path!
			if [[ "${compile_srctree}" == "${compile_srctree##/}" ]]; then
				dir_sources="${PWD}/${compile_srctree}"
			else
				dir_sources="${compile_srctree}"
			fi
		elif [[ -e "${default_sources_dir}/linux-${compile_srctree}/include/linux/kernel.h" ]]; then
			# user meant a tree prepared for kcbench by just giving version number
			dir_sources="${default_sources_dir%%/}/linux-${compile_srctree%%/}/"
		else
			echo "Could neither find directory ${compile_srctree} nor ${default_sources_dir%%/}/linux-${compile_srctree%%/}/" >&2
			exit 2
		fi
	else
		# we need to decide own our own

		# without our std-dir it doesn't make sense to continue
		if [[ ! -d "${default_sources_dir}" ]]; then
			echo "${default_sources_dir} not found." >&2
			exit 2
		fi

		# use the latest one by default
		dir_sources="${default_sources_dir%%/}/$(ls -r "${default_sources_dir}" | head -n 1)"
		if [[ ! -e "${dir_sources}/include/linux/kernel.h" ]]; then
			echo "Found ${default_sources_dir}, but doesn't look like a kernel srctree." >&2
			exit 2
		fi
	fi


	# on testsystems the date sometimes is set incorectly
	# check date prevent misscompiles
	if (( $(date -r "${dir_sources}/Makefile" "+%s") > $(date "+%s") )); then
		echo "Makefile is younger then current date; please set your system time properly." >&2
		exit 2
	fi


	# create tempdir and remove it on exit
	trap 'kcbench_exit 127' 1 2 15
	if [[ "${dir_topoutput}" ]]; then
		# dir_topoutput present?
		if [[ ! -d "${dir_topoutput}" ]]; then
			echo "Could not find ${dir_topoutput}" >&2
			exit 2
		fi
		if [[ "${dir_topoutput}" == "${dir_topoutput##/}" ]]; then
			# we need the full path
			dir_topoutput="${PWD}/${dir_topoutput}"
		fi

		# if the kcbench dir exists in dir_topoutput already just use it
		if [[ ! -d "${dir_topoutput%%/}/${myprog_name}" ]]; then
			if ! mkdir ${dir_topoutput%%/}/${myprog_name}; then
				echo "Could not create ${dir_topoutput%%/}/${myprog_name}" >&2
				exit 2
			fi
		fi
		readonly dir_outputtmp="${dir_topoutput%%/}/${myprog_name}"

	else
		# use a tmp dir
		readonly dir_outputtmp=$(mktemp -d -t ${myprog_name}.XXXXXXXXX)
	fi

	# was dir created?
	local returncode=$?
	if (( "${returncode}" > 0 )) || [[ ! -d "${dir_outputtmp}" ]]; then
		echo "Could not create temporary output directory" >&2 
		exit 2
	fi

	# save logfiles somewhere?
	if [[ "${savefailedlogs}" ]] && [[ ! -d "${savefailedlogs}" ]] ; then
		echo "Could not find ${savefailedlogs}." >&2 
		exit 2
	fi


	# how many jobs to use?
	if [[ ! "${number_of_jobs}" ]]; then
		# use default
		number_of_jobs=${default_number_of_jobs}
	else
		# user provided number of jobs; but check user input
		for number in ${number_of_jobs}; do
			# is number_of_jobs a real number?
			if (( ! ${number} > 0 )) ; then
				echo "Please provide a real number together with --jobs" >&2
				exit 2
			fi
		done
	fi


	# is number_of_iterations a real number?
	if [[ ! "${number_of_iterations}" ]] ; then
		# use default
		if [[ "${run_infinite}" ]]; then 
			# when running infinite just 1
			number_of_iterations=1
		else
			number_of_iterations=${default_number_of_iterations}
		fi
	elif (( ! ${number_of_iterations} > 0 )) ; then
		echo "Please provide a real number together with --ilterations" >&2
		exit 2
	fi


	# create a logdirectory in dir_outputtmp
	[[ ! -d "${dir_outputtmp}/kcbench" ]] && mkdir "${dir_outputtmp}/kcbench"
	kcbench_logdir="${dir_outputtmp}/kcbench/"
	kcbench_logfile="${dir_outputtmp}/kcbench/log"
	touch "${kcbench_logfile}"


	# disable sceensaver (should we reset this once we finished? how?)
	setterm -blank 0
}


kcbench_exit()
{
	kcbench_echo 1 4 "Removing ${dir_outputtmp}"

	if [[ "${dir_outputtmp}" ]] && [[ -d "${dir_outputtmp}" ]]; then
		# the directory should contain our name, thus check it to be on the safe side
		if echo "${dir_outputtmp}" | grep "${myprog_name}" &> /dev/null ; then
			rm -rf "${dir_outputtmp}"
		else
			echo "Leaving ${dir_outputtmp} behind" >&2
		fi
	fi

	exit ${1}
}

kcbench_echo ()
{
	# where to output
	local this_fd=${1}
	shift

	# verboselevel
	local this_verbose=${1}
	shift

	# output
	if (( ${verboselevel} >= ${this_verbose} )); then
		echo "$@" >&${this_fd}
		echo "$@" >> "${kcbench_logfile}"
	fi
}

kcbench_main()
{
	# info
	kcebench_sysinfo 2

	# create defconfig
	kcbench_echo 1 3 "Creating default configuration with 'make defconfig'."
	make O="${dir_outputtmp}" -C "${dir_sources}" -j ${default_number_of_jobs} defconfig >> "${kcbench_logdir}"/make_defconfig 2>&1

	# prepration run
	if [[ ! "${no_cachefill}" ]]; then
		# Note: I tried to use something like this first:
		# find "${dir_sources}" -type f | grep -e '.h$'  -e '.c$' -e 'Makefile' -e 'Kconfig' -e 'Kbuild' -e '.S$' | xargs cat > /dev/null
		# But it does not work as good as a thrown-away compile-run

		# only print result in verbose mode
		if [[ "${show_result_from_cache_free_run}" ]]; then
			kcbench_compile_kernel 2 ${default_number_of_jobs} run-0-fillcaches "Filling caches:     "
		else
			(( ${verboselevel} == 2 )) && kcbench_echo 1 2 -n "Filling caches:     This might take a while... "
			kcbench_compile_kernel 3 ${default_number_of_jobs} run-0-fillcaches "Fill cache run (${default_number_of_jobs})"
			kcbench_echo 1 3 "-------------------------------------------------------------------------------"
		fi
	fi

	# go
	local totalruns=1
	local errorruns=0
	while : ; do
		for number in ${number_of_jobs}; do
			for ((run=1; run <= number_of_iterations ; run++)) ; do
				kcbench_compile_kernel 1 ${number} "run${totalruns}" "$(printf "%-20s" "Run ${totalruns} (-j ${number}):")"
				local returncode=$?
				if (( ${returncode} > 0 )); then
					let errorruns++
				fi
				let totalruns++
			done
		done

		if (( ${errorruns} > 0 )) ; then
			kcbench_echo 1 1 "Total # of errors:  ${errorruns}"
		fi

		if [[ ! "${run_infinite}" ]]; then
			# get out of the while loop
			break
		fi
	done
}

parse_results ()
{
	local this_resultfile="${1}"
	local this_verboselevel="${2}"
	local this_runtype="${3}"

	while read format value; do
		case "${format}" in
			E)
				local tm_elapsed="${value}"
				;;
			e)
				local tm_elapsed_s="${value}"
				;;
			S)
				local tm_kernel_s="${value}"
				;;
			U)
				local tm_user_s="${value}"
				;;
			P)
				local tm_cpu_p="${value}"
				;;
			M)
				local tm_mem_max="${value}"
				;;
			F)
				local tm_pgfault_maj="${value}"
				;;
			R)
				local tm_pgfault_min="${value}"
				;;
			W)
				local tm_swapped="${value}"
				;;
			c)
				local tm_cswitched="${value}"
				;;
			w)
				local tm_waits="${value}"
				;;
			I)
				local tm_file_in="${value}"
				;;
			O)
				local tm_file_out="${value}"
				;;
			x)
				local tm_exit="${value}"
				;;
		esac
	done < "${this_resultfile}"

	if [[ "${this_runtype}" != "run-0-fillcaches" ]]; then
		local time_points="$(echo 1000000/${tm_elapsed_s} | /usr/bin/bc)"
		kcbench_echo 1 ${this_verboselevel} "${time_points} (e:${tm_elapsed_s} P:${tm_cpu_p} U:${tm_user_s} S:${tm_kernel_s} F:${tm_pgfault_maj})"
		if [[ "${print_detailed_results}" ]]; then
			echo "  Elapsed Time(E): ${tm_elapsed} (${tm_elapsed_s} seconds)"
			echo "  Kernel time (S): ${tm_kernel_s} seconds"
			echo "  User time (U): ${tm_user_s} seconds"
			echo "  CPU usage (P): ${tm_cpu_p}"
			echo "  Major page faults (F): ${tm_pgfault_maj}"
			echo "  Minor page faults (R): ${tm_pgfault_min}"
			echo "  Context switches involuntarily (c): ${tm_cswitched}"
			echo "  Context switches voluntarily (w): ${tm_waits}"
		fi
	else
		if [[ "${show_result_from_cache_free_run}" ]]; then
			kcbench_echo 1 2 "Done (-j ${default_number_of_jobs}, e: ${tm_elapsed_s})"
		else
			kcbench_echo 1 2 "Done"
		fi
	fi
}

kcbench_cleanup_builddir()
{
	# cleanup
	kcbench_echo 1 3 "Cleaning up builddir"
	make O="${dir_outputtmp}" -C "${dir_sources}" -j ${default_number_of_jobs} clean &> "${kcbench_logdir}/make_clean"
}

kcbench_compile_kernel()
{
	local this_verboselevel="${1}"
	local this_nrjobs="${2}"
	local this_runtype="${3}"
	local this_logfile="${kcbench_logdir%%/}/${3}"
	local this_msgstart="${4}"

	echo "make O=${dir_outputtmp} -C ${dir_sources} -j ${this_nrjobs} vmlinux" > "${this_logfile}"
	kcbench_echo 1 4 "Running 'make O=${dir_outputtmp} -C ${dir_sources} -j ${this_nrjobs} vmlinux'"

	kcbench_echo 1 ${this_verboselevel} -n "${this_msgstart}"

	if /usr/bin/time -o "${this_logfile}.time" -f 'E %E\ne %e\nS %S\nU %U\nP %P\nM %M\nF %F\nR %R\nW %W\nc %c\nw %w\nI %I\nO %O\nx %x' make O="${dir_outputtmp}" -C "${dir_sources}" -j ${this_nrjobs} vmlinux >> "${this_logfile}" 2>&1 ; then
	#if /usr/bin/time -o "${this_logfile}.time" -f 'E %E\ne %e\nS %S\nU %U\nP %P\nM %M\nF %F\nR %R\nW %W\nc %c\nw %w\nI %I\nO %O\nx %x' dd if=/dev/urandom bs=10M count=1 2>/dev/null | md5sum >> "${this_logfile}" 2>&1 ; then	
		parse_results "${this_logfile}.time" ${this_verboselevel} "${this_runtype}"
		kcbench_cleanup_builddir
	else
		kcbench_echo 2 1 "Failed ($(date))."
		kcbench_cleanup_builddir

		if [[ "${savefailedlogs}" ]]; then
			# safe log and continue
			kcbench_echo 2 2 "Saving logfile to ${savefailedlogs}/kcbench-$(basename ${this_logfile})"
			cp "${this_logfile}" "${savefailedlogs}/kcbench-$(basename ${this_logfile})"
		fi

		if [[ "${run_infinite}" ]]; then
			return 1
		else
			# what to do with the logfile?
			read -n 1 -s -t 300 -p "Hit 'l' within 300 seconds to view log-file." answer
			echo

			if [[ "${answer}" == [lL] ]]; then
				less "${this_logfile}" 
			fi

			# end
			kcbench_exit 2
		fi
	fi
}

# basic information about the system
kcebench_sysinfo()
{
	kcbench_echo 1 ${1} "Linux running:      $(uname -r) on $(uname -i) ($(uname -n))"
	kcbench_echo 1 ${1} "Processor Cores:    $(grep '^processor' < /proc/cpuinfo  | wc -l) -- $(grep 'model name' /proc/cpuinfo  | sed 's!model name\t: !!' | sort | uniq)"
	kcbench_echo 1 ${1} "Compiler:           $(gcc --version | head -n 1)"
	[[ -r /proc/memory ]] && kcbench_echo 1 ${1} "Memory:             $(( $(awk '/MemTotal:/ { print $2}' /proc/meminfo) / 1024  )) MByte"
	kcbench_echo 1 ${1} "Linux compiled:     $(basename "${dir_sources}") (${dir_sources})"
}

myprog_help()
{
	echo "Usage: ${myprog_name} [options]"
	echo $'\n'"Compiles a kernel and messures the time it takes"
	echo $'\n'"Available options:"
	echo " -d, --compiledir <path>      -- use <path>/kcbench for compile results (O=)"
	echo " -r, --detailedresults        -- print more detailed results"
	echo " -a, --ignore-running-apps    -- Do not warn if cron or other daemons run"
	echo " -i, --infinite               -- run endlessly"
	echo " -n, --iterations <int>       -- number or iterations"
	echo " -j, --jobs <int>             -- number of jobs to use ('make -j #') (*)"
	echo " -c, --no-cachefill           -- omit the initial kernel compile to fill caches"
	echo " -q, --quiet                  -- quiet"
	echo " -v, --verbose                -- increase verboselevel (*)"
	echo " -l, --savefailedlogs <path>  -- save log of failed compile runs in <path>"
	echo " -s, --src (<path>|<version>) -- take sources in <path> or from"
	echo "                                 /usr/share/kcdata/linux-<version>"
	echo
	echo " -h, --help                   -- this text"
	echo " -V, --version                -- output program version"
	echo
	echo "(*) -- option can be passed multiple times"
}


# set defaults which might get overwritten my command line parameters
kcbench_init


# parse cmdline options
while [ "${1}" ] ; do
	case "${1}" in
		-d|--compiledir)
			shift
			dir_topoutput="${1}"
			shift
			;;
		-r|--detailedresults)
			shift
			print_detailed_results="true"
			;;
		-j|--jobs)
			shift
			# without quotes, to make --jobs "4 8 16 32" possible
			number_of_jobs="${number_of_jobs} ${1}"
			shift
			;;
		-a|--ignore-running-apps)
			shift
			ignore_running_apps="true"
			;;
		-i|--infinite)
			shift
			run_infinite="true"
			;;
		-n|--iterations)
			shift
			number_of_iterations="${1}"
			shift
			;;
		-c|--no-cachefill)
			shift
			no_cachefill="true"
			;;
		-q|--quiet)
			verboselevel="1"
			shift
			;;
		-v|--verbose)
			shift
			let verboselevel++
			;;
		-l|--savefailedlogs)
			shift
			savefailedlogs="${1}"
			shift
			;;
		-s|--src)
			shift
			compile_srctree="${1}"
			shift
			;;
		-h|--help)
			myprog_help
			exit 0
			;;
		-V|--version)
			echo "${myprog_name} ${myprog_version}"
			exit 0
			;;
		--*)
			echo "Error: Unknown option '${1}'." >&2
			myprog_help >&2
			exit 2
			;;
		*)
			list_of_packages="${list_of_packages} ${1}"
			shift
			;;
	esac
done

# startup checks
kcbench_startupchecks

# go
kcbench_main

# cleanup
kcbench_exit

exit 0