From: Jiri Olsa <jolsa@redhat.com>
To: KP Singh <kpsingh@kernel.org>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Florent Revest <revest@chromium.org>,
Brendan Jackman <jackmanb@chromium.org>
Subject: Re: [PATCH bpf-next v2 1/2] bpf: Helper script for running BPF presubmit tests
Date: Mon, 25 Jan 2021 23:19:05 +0100 [thread overview]
Message-ID: <20210125221905.GB9662@krava> (raw)
In-Reply-To: <20210123004445.299149-2-kpsingh@kernel.org>
On Sat, Jan 23, 2021 at 12:44:44AM +0000, KP Singh wrote:
> The script runs the BPF selftests locally on the same kernel image
> as they would run post submit in the BPF continuous integration
> framework.
>
> The goal of the script is to allow contributors to run selftests locally
> in the same environment to check if their changes would end up breaking
> the BPF CI and reduce the back-and-forth between the maintainers and the
> developers.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
great! thanks for this
Tested-by: Jiri Olsa <jolsa@redhat.com>
jirka
> ---
> tools/testing/selftests/bpf/run_in_vm.sh | 353 +++++++++++++++++++++++
> 1 file changed, 353 insertions(+)
> create mode 100755 tools/testing/selftests/bpf/run_in_vm.sh
>
> diff --git a/tools/testing/selftests/bpf/run_in_vm.sh b/tools/testing/selftests/bpf/run_in_vm.sh
> new file mode 100755
> index 000000000000..09bb9705acb3
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/run_in_vm.sh
> @@ -0,0 +1,353 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +set -u
> +set -e
> +
> +QEMU_BINARY="${QEMU_BINARY:="qemu-system-x86_64"}"
> +X86_BZIMAGE="arch/x86/boot/bzImage"
> +DEFAULT_COMMAND="./test_progs"
> +MOUNT_DIR="mnt"
> +ROOTFS_IMAGE="root.img"
> +OUTPUT_DIR="$HOME/.bpf_selftests"
> +KCONFIG_URL="https://raw.githubusercontent.com/libbpf/libbpf/master/travis-ci/vmtest/configs/latest.config"
> +INDEX_URL="https://raw.githubusercontent.com/libbpf/libbpf/master/travis-ci/vmtest/configs/INDEX"
> +NUM_COMPILE_JOBS="$(nproc)"
> +
> +usage()
> +{
> + cat <<EOF
> +Usage: $0 [-k] [-i] [-d <output_dir>] -- [<command>]
> +
> +<command> is the command you would normally run when you are in
> +tools/testing/selftests/bpf. e.g:
> +
> + $0 -- ./test_progs -t test_lsm
> +
> +If no command is specified, "${DEFAULT_COMMAND}" will be run by
> +default.
> +
> +If you build your kernel using KBUILD_OUTPUT= or O= options, these
> +can be passed as environment variables to the script:
> +
> + O=<path_relative_to_cwd> $0 -- ./test_progs -t test_lsm
> +
> +or
> +
> + KBUILD_OUTPUT=<path_relative_to_cwd> $0 -- ./test_progs -t test_lsm
> +
> +Options:
> +
> + -k) "Keep the kernel", i.e. don't recompile the kernel if it exists.
> + -i) Update the rootfs image with a newer version.
> + -d) Update the output directory (default: ${OUTPUT_DIR})
> + -j) Number of jobs for compilation, similar to -j in make
> + (default: ${NUM_COMPILE_JOBS})
> +EOF
> +}
> +
> +unset URLS
> +populate_url_map()
> +{
> + if ! declare -p URLS &> /dev/null; then
> + # URLS contain the mapping from file names to URLs where
> + # those files can be downloaded from.
> + declare -gA URLS
> + while IFS=$'\t' read -r name url; do
> + URLS["$name"]="$url"
> + done < <(curl -Lsf ${INDEX_URL})
> + fi
> + echo "${URLS[*]}"
> +}
> +
> +download()
> +{
> + local file="$1"
> +
> + if [[ ! -v URLS[$file] ]]; then
> + echo "$file not found" >&2
> + return 1
> + fi
> +
> + echo "Downloading $file..." >&2
> + curl -Lf "${URLS[$file]}" "${@:2}"
> +}
> +
> +newest_rootfs_version()
> +{
> + {
> + for file in "${!URLS[@]}"; do
> + if [[ $file =~ ^libbpf-vmtest-rootfs-(.*)\.tar\.zst$ ]]; then
> + echo "${BASH_REMATCH[1]}"
> + fi
> + done
> + } | sort -rV | head -1
> +}
> +
> +download_rootfs()
> +{
> + local rootfsversion="$1"
> + local dir="$2"
> +
> + if ! which zstd &> /dev/null; then
> + echo 'Could not find "zstd" on the system, please install zstd'
> + exit 1
> + fi
> +
> + download "libbpf-vmtest-rootfs-$rootfsversion.tar.zst" |
> + zstd -d | sudo tar -C "$dir" -x
> +}
> +
> +recompile_kernel()
> +{
> + local kernel_checkout="$1"
> + local make_command="$2"
> +
> + cd "${kernel_checkout}"
> +
> + ${make_command} olddefconfig
> + ${make_command}
> +}
> +
> +mount_image()
> +{
> + local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
> + local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
> +
> + sudo mount -o loop "${rootfs_img}" "${mount_dir}"
> +}
> +
> +unmount_image()
> +{
> + local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
> +
> + sudo umount "${mount_dir}" &> /dev/null
> +}
> +
> +update_selftests()
> +{
> + local kernel_checkout="$1"
> + local selftests_dir="${kernel_checkout}/tools/testing/selftests/bpf"
> +
> + cd "${selftests_dir}"
> + ${make_command}
> +
> + # Mount the image and copy the selftests to the image.
> + mount_image
> + sudo rm -rf "${mount_dir}/root/bpf"
> + sudo cp -r "${selftests_dir}" "${mount_dir}/root"
> + unmount_image
> +}
> +
> +update_init_script()
> +{
> + local init_script_dir="${OUTPUT_DIR}/${MOUNT_DIR}/etc/rcS.d"
> + local init_script="${init_script_dir}/S50-startup"
> + local command="$1"
> + local log_file="$2"
> +
> + mount_image
> +
> + if [[ ! -d "${init_script_dir}" ]]; then
> + cat <<EOF
> +Could not find ${init_script_dir} in the mounted image.
> +This likely indicates a bad rootfs image, Please download
> +a new image by passing "-i" to the script
> +EOF
> + exit 1
> +
> + fi
> +
> + cat <<EOF | sudo tee "${init_script}"
> +#!/bin/bash
> +
> +{
> +
> + cd /root/bpf
> + echo ${command}
> + ${command}
> +} 2>&1 | tee /root/${log_file}
> +poweroff -f
> +EOF
> +
> + sudo chmod a+x "${init_script}"
> + unmount_image
> +}
> +
> +create_vm_image()
> +{
> + local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
> + local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
> +
> + rm -rf "${rootfs_img}"
> + touch "${rootfs_img}"
> + chattr +C "${rootfs_img}" >/dev/null 2>&1 || true
> +
> + truncate -s 2G "${rootfs_img}"
> + mkfs.ext4 -q "${rootfs_img}"
> +
> + mount_image
> + download_rootfs "$(newest_rootfs_version)" "${mount_dir}"
> + unmount_image
> +}
> +
> +run_vm()
> +{
> + local kernel_bzimage="$1"
> + local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
> +
> + if ! which "${QEMU_BINARY}" &> /dev/null; then
> + cat <<EOF
> +Could not find ${QEMU_BINARY}
> +Please install qemu or set the QEMU_BINARY environment variable.
> +EOF
> + exit 1
> + fi
> +
> + ${QEMU_BINARY} \
> + -nodefaults \
> + -display none \
> + -serial mon:stdio \
> + -cpu kvm64 \
> + -enable-kvm \
> + -smp 4 \
> + -m 2G \
> + -drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
> + -kernel "${kernel_bzimage}" \
> + -append "root=/dev/vda rw console=ttyS0,115200"
> +}
> +
> +copy_logs()
> +{
> + local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
> + local log_file="${mount_dir}/root/$1"
> +
> + mount_image
> + sudo cp ${log_file} "${OUTPUT_DIR}"
> + sudo rm -f ${log_file}
> + unmount_image
> +}
> +
> +is_rel_path()
> +{
> + local path="$1"
> +
> + [[ ${path:0:1} != "/" ]]
> +}
> +
> +main()
> +{
> + local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
> + local kernel_checkout=$(realpath "${script_dir}"/../../../../)
> + local log_file="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S.log")"
> + # By default the script searches for the kernel in the checkout directory but
> + # it also obeys environment variables O= and KBUILD_OUTPUT=
> + local kernel_bzimage="${kernel_checkout}/${X86_BZIMAGE}"
> + local command="${DEFAULT_COMMAND}"
> + local kernel_recompile="yes"
> + local update_image="no"
> +
> + while getopts 'hkid:j:' opt; do
> + case ${opt} in
> + k)
> + kernel_recompile="no"
> + ;;
> + i)
> + update_image="yes"
> + ;;
> + d)
> + OUTPUT_DIR="$OPTARG"
> + ;;
> + j)
> + NUM_COMPILE_JOBS="$OPTARG"
> + ;;
> + h)
> + usage
> + exit 0
> + ;;
> + \? )
> + echo "Invalid Option: -$OPTARG"
> + usage
> + exit 1
> + ;;
> + : )
> + echo "Invalid Option: -$OPTARG requires an argument"
> + usage
> + exit 1
> + ;;
> + esac
> + done
> + shift $((OPTIND -1))
> +
> + if [[ $# -eq 0 ]]; then
> + echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
> + else
> + command="$@"
> + fi
> +
> + local kconfig_file="${OUTPUT_DIR}/latest.config"
> + local make_command="make -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}"
> +
> + # Figure out where the kernel is being built.
> + # O takes precedence over KBUILD_OUTPUT.
> + if [[ "${O:=""}" != "" ]]; then
> + if is_rel_path "${O}"; then
> + O="$(realpath "${PWD}/${O}")"
> + fi
> + kernel_bzimage="${O}/${X86_BZIMAGE}"
> + make_command="${make_command} O=${O}"
> + elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then
> + if is_rel_path "${KBUILD_OUTPUT}"; then
> + KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")"
> + fi
> + kernel_bzimage="${KBUILD_OUTPUT}/${X86_BZIMAGE}"
> + make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"
> + fi
> +
> + populate_url_map
> +
> + local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
> + local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
> +
> + echo "Output directory: ${OUTPUT_DIR}"
> +
> + mkdir -p "${OUTPUT_DIR}"
> + mkdir -p "${mount_dir}"
> + curl -Lf "${KCONFIG_URL}" -o "${kconfig_file}"
> +
> + if [[ "${kernel_recompile}" == "no" && ! -f "${kernel_bzimage}" ]]; then
> + echo "Kernel image not found in ${kernel_bzimage}, kernel will be recompiled"
> + kernel_recompile="yes"
> + fi
> +
> + if [[ "${kernel_recompile}" == "yes" ]]; then
> + recompile_kernel "${kernel_checkout}" "${make_command}"
> + fi
> +
> + if [[ "${update_image}" == "no" && ! -f "${rootfs_img}" ]]; then
> + echo "rootfs image not found in ${rootfs_img}"
> + update_image="yes"
> + fi
> +
> + if [[ "${update_image}" == "yes" ]]; then
> + create_vm_image
> + fi
> +
> + update_selftests "${kernel_checkout}" "${make_command}"
> + update_init_script "${command}" "${log_file}"
> + run_vm "${kernel_bzimage}"
> + copy_logs "${log_file}"
> + echo "Logs saved in ${OUTPUT_DIR}/${log_file}"
> +}
> +
> +catch()
> +{
> + local exit_code=$1
> +
> + unmount_image
> + exit ${exit_code}
> +}
> +
> +trap 'catch "$?"' EXIT
> +
> +main "$@"
> --
> 2.30.0.280.ga3ce27912f-goog
>
next prev parent reply other threads:[~2021-01-25 22:21 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-01-23 0:44 [PATCH bpf-next v2 0/2] BPF selftest helper script KP Singh
2021-01-23 0:44 ` [PATCH bpf-next v2 1/2] bpf: Helper script for running BPF presubmit tests KP Singh
2021-01-24 19:06 ` Yonghong Song
2021-01-25 5:21 ` Yonghong Song
2021-01-25 17:53 ` KP Singh
2021-01-25 19:42 ` Yonghong Song
2021-01-25 17:51 ` KP Singh
2021-01-25 22:19 ` Jiri Olsa [this message]
2021-01-26 2:09 ` Andrii Nakryiko
2021-01-26 19:51 ` Jiri Olsa
2021-02-01 0:22 ` KP Singh
2021-02-02 6:25 ` Andrii Nakryiko
2021-02-02 21:13 ` KP Singh
2021-02-02 21:37 ` Andrii Nakryiko
2021-02-01 0:40 ` KP Singh
2021-01-23 0:44 ` [PATCH bpf-next v2 2/2] bpf/selftests: Add a short note about run_in_vm.sh in README.rst KP Singh
2021-01-24 19:14 ` Yonghong Song
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210125221905.GB9662@krava \
--to=jolsa@redhat.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=jackmanb@chromium.org \
--cc=kpsingh@kernel.org \
--cc=revest@chromium.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox