mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 01:03:07 +00:00
vmtest: don't use BusyBox
We don't specifically need BusyBox; we just need a reasonable Linux userspace, which we can assume is already available on the host, whether it's coreutils+util-linux, BusyBox, or something else. Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
parent
1b8d0ae82b
commit
b535b8f82e
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install busybox-static libelf-dev libdw-dev qemu-kvm zstd ${{ matrix.cc == 'clang' && 'libomp-$(clang --version | sed -rn "s/.*clang version ([0-9]+).*/\\1/p")-dev' || '' }}
|
sudo apt-get install libelf-dev libdw-dev qemu-kvm zstd ${{ matrix.cc == 'clang' && 'libomp-$(clang --version | sed -rn "s/.*clang version ([0-9]+).*/\\1/p")-dev' || '' }}
|
||||||
pip install pyroute2 pre-commit
|
pip install pyroute2 pre-commit
|
||||||
- name: Generate version.py
|
- name: Generate version.py
|
||||||
run: python setup.py --version
|
run: python setup.py --version
|
||||||
|
4
setup.py
4
setup.py
@ -258,10 +258,10 @@ class test(Command):
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
export DRGN_TEST_KMOD={shlex.quote(str(kmod))}
|
export DRGN_TEST_KMOD={shlex.quote(str(kmod))}
|
||||||
if "$BUSYBOX" [ -e /proc/vmcore ]; then
|
if [ -e /proc/vmcore ]; then
|
||||||
"$PYTHON" -Bm unittest discover -t . -s tests/linux_kernel/vmcore {"-v" if self.verbose else ""}
|
"$PYTHON" -Bm unittest discover -t . -s tests/linux_kernel/vmcore {"-v" if self.verbose else ""}
|
||||||
else
|
else
|
||||||
"$BUSYBOX" insmod "$DRGN_TEST_KMOD"
|
insmod "$DRGN_TEST_KMOD"
|
||||||
DRGN_RUN_LINUX_KERNEL_TESTS=1 "$PYTHON" -Bm \
|
DRGN_RUN_LINUX_KERNEL_TESTS=1 "$PYTHON" -Bm \
|
||||||
unittest discover -t . -s tests/linux_kernel {"-v" if self.verbose else ""}
|
unittest discover -t . -s tests/linux_kernel {"-v" if self.verbose else ""}
|
||||||
"$PYTHON" vmtest/enter_kdump.py
|
"$PYTHON" vmtest/enter_kdump.py
|
||||||
|
@ -4,8 +4,8 @@ drgn VM Testing
|
|||||||
drgn has a significant amount of code (both core and in helpers) which is
|
drgn has a significant amount of code (both core and in helpers) which is
|
||||||
dependent on the Linux kernel version. This code is tested on multiple Linux
|
dependent on the Linux kernel version. This code is tested on multiple Linux
|
||||||
kernel versions in a virtual machine. These tests can be run on all supported
|
kernel versions in a virtual machine. These tests can be run on all supported
|
||||||
kernels with ``python3 setup.py test -K``. This requires QEMU, BusyBox, and
|
kernels with ``python3 setup.py test -K``. This requires QEMU and zstd to be
|
||||||
zstd to be installed.
|
installed.
|
||||||
|
|
||||||
Tests can also be run on specific kernels with ``-k``. This takes a
|
Tests can also be run on specific kernels with ``-k``. This takes a
|
||||||
comma-separated list of kernels which are wildcard patterns (e.g., ``5.6.*``)
|
comma-separated list of kernels which are wildcard patterns (e.g., ``5.6.*``)
|
||||||
@ -25,9 +25,8 @@ safety. To support modifications, the guest uses `OverlayFS
|
|||||||
overlay a read-write tmpfs over the VirtFS root. It also mounts the kernel
|
overlay a read-write tmpfs over the VirtFS root. It also mounts the kernel
|
||||||
modules and vmlinux via VirtFS.
|
modules and vmlinux via VirtFS.
|
||||||
|
|
||||||
The guest runs a `BusyBox <https://www.busybox.net/>`_ shell script as init
|
The guest runs an init shell script which sets up the system and filesystem
|
||||||
which sets up the system and filesystem hierarchy, runs a command, and returns
|
hierarchy, runs a command, and returns the exit status via `virtio-serial
|
||||||
the exit status via `virtio-serial
|
|
||||||
<https://fedoraproject.org/wiki/Features/VirtioSerial>`_.
|
<https://fedoraproject.org/wiki/Features/VirtioSerial>`_.
|
||||||
|
|
||||||
This infrastructure is all generic. The drgn-specific parts are:
|
This infrastructure is all generic. The drgn-specific parts are:
|
||||||
|
116
vmtest/vm.py
116
vmtest/vm.py
@ -1,12 +1,10 @@
|
|||||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import errno
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@ -16,110 +14,114 @@ from util import nproc, out_of_date
|
|||||||
|
|
||||||
_9PFS_MSIZE = 1024 * 1024
|
_9PFS_MSIZE = 1024 * 1024
|
||||||
|
|
||||||
# Script run as init in the virtual machine. This only depends on busybox. We
|
# Script run as init in the virtual machine.
|
||||||
# don't assume that any regular commands are built in (not even echo or test),
|
_INIT_TEMPLATE = r"""#!/bin/sh
|
||||||
# so we always explicitly run busybox.
|
|
||||||
_INIT_TEMPLATE = r"""#!{busybox} sh
|
# Having /proc from the host visible in the guest can confuse some commands. In
|
||||||
|
# particular, if BusyBox is configured with FEATURE_SH_STANDALONE, then busybox
|
||||||
|
# sh executes BusyBox applets using /proc/self/exe. So, before doing anything
|
||||||
|
# else, mount /proc (using the fully qualified executable path so that BusyBox
|
||||||
|
# doesn't use /proc/self/exe).
|
||||||
|
/bin/mount -t proc -o nosuid,nodev,noexec proc /proc
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
export BUSYBOX={busybox}
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
export PYTHON={python}
|
export PYTHON={python}
|
||||||
{kdump_needs_nosmp}
|
{kdump_needs_nosmp}
|
||||||
|
|
||||||
trap '"$BUSYBOX" poweroff -f' EXIT
|
trap 'poweroff -f' EXIT
|
||||||
|
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
HOSTNAME=vmtest
|
HOSTNAME=vmtest
|
||||||
VPORT_NAME=com.osandov.vmtest.0
|
VPORT_NAME=com.osandov.vmtest.0
|
||||||
RELEASE=$("$BUSYBOX" uname -r)
|
RELEASE=$(uname -r)
|
||||||
|
|
||||||
# Set up overlayfs on the temporary directory containing this script.
|
# Set up overlayfs on the temporary directory containing this script.
|
||||||
mnt=$("$BUSYBOX" dirname "$0")
|
mnt=$(dirname "$0")
|
||||||
"$BUSYBOX" mount -t tmpfs tmpfs "$mnt"
|
mount -t tmpfs tmpfs "$mnt"
|
||||||
"$BUSYBOX" mkdir "$mnt/upper" "$mnt/work" "$mnt/merged"
|
mkdir "$mnt/upper" "$mnt/work" "$mnt/merged"
|
||||||
|
|
||||||
"$BUSYBOX" mkdir "$mnt/upper/dev" "$mnt/upper/etc" "$mnt/upper/mnt"
|
mkdir "$mnt/upper/dev" "$mnt/upper/etc" "$mnt/upper/mnt"
|
||||||
"$BUSYBOX" mkdir -m 555 "$mnt/upper/proc" "$mnt/upper/sys"
|
mkdir -m 555 "$mnt/upper/proc" "$mnt/upper/sys"
|
||||||
"$BUSYBOX" mkdir -m 1777 "$mnt/upper/tmp"
|
mkdir -m 1777 "$mnt/upper/tmp"
|
||||||
|
|
||||||
# Create configuration files.
|
mount -t overlay -o lowerdir=/,upperdir="$mnt/upper",workdir="$mnt/work" overlay "$mnt/merged"
|
||||||
"$BUSYBOX" cat << EOF > "$mnt/upper/etc/hosts"
|
|
||||||
127.0.0.1 localhost
|
|
||||||
::1 localhost
|
|
||||||
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
|
|
||||||
EOF
|
|
||||||
: > "$mnt/upper/etc/resolv.conf"
|
|
||||||
|
|
||||||
"$BUSYBOX" mount -t overlay -o lowerdir=/,upperdir="$mnt/upper",workdir="$mnt/work" overlay "$mnt/merged"
|
# Mount core filesystems.
|
||||||
"$BUSYBOX" pivot_root "$mnt/merged" "$mnt/merged/mnt"
|
mount -t devtmpfs -o nosuid,noexec dev "$mnt/merged/dev"
|
||||||
cd /
|
mkdir "$mnt/merged/dev/shm"
|
||||||
"$BUSYBOX" umount -l /mnt
|
mount -t tmpfs -o nosuid,nodev tmpfs "$mnt/merged/dev/shm"
|
||||||
|
mount -t proc -o nosuid,nodev,noexec proc "$mnt/merged/proc"
|
||||||
# Mount additional filesystems.
|
mount -t sysfs -o nosuid,nodev,noexec sys "$mnt/merged/sys"
|
||||||
"$BUSYBOX" mount -t devtmpfs -o nosuid,noexec dev /dev
|
|
||||||
"$BUSYBOX" mkdir /dev/shm
|
|
||||||
"$BUSYBOX" mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm
|
|
||||||
"$BUSYBOX" mount -t proc -o nosuid,nodev,noexec proc /proc
|
|
||||||
"$BUSYBOX" mount -t sysfs -o nosuid,nodev,noexec sys /sys
|
|
||||||
# cgroup2 was added in Linux v4.5.
|
# cgroup2 was added in Linux v4.5.
|
||||||
"$BUSYBOX" mount -t cgroup2 -o nosuid,nodev,noexec cgroup2 /sys/fs/cgroup || "$BUSYBOX" true
|
mount -t cgroup2 -o nosuid,nodev,noexec cgroup2 "$mnt/merged/sys/fs/cgroup" || true
|
||||||
# Ideally we'd just be able to create an opaque directory for /tmp on the upper
|
# Ideally we'd just be able to create an opaque directory for /tmp on the upper
|
||||||
# layer. However, before Linux kernel commit 51f7e52dc943 ("ovl: share inode
|
# layer. However, before Linux kernel commit 51f7e52dc943 ("ovl: share inode
|
||||||
# for hard link") (in v4.8), overlayfs doesn't handle hard links correctly,
|
# for hard link") (in v4.8), overlayfs doesn't handle hard links correctly,
|
||||||
# which breaks some tests.
|
# which breaks some tests.
|
||||||
"$BUSYBOX" mount -t tmpfs -o nosuid,nodev tmpfs /tmp
|
mount -t tmpfs -o nosuid,nodev tmpfs "$mnt/merged/tmp"
|
||||||
|
|
||||||
|
# Pivot into the new root.
|
||||||
|
pivot_root "$mnt/merged" "$mnt/merged/mnt"
|
||||||
|
cd /
|
||||||
|
umount -l /mnt
|
||||||
|
|
||||||
# Load kernel modules.
|
# Load kernel modules.
|
||||||
"$BUSYBOX" mkdir -p "/lib/modules/$RELEASE"
|
mkdir -p "/lib/modules/$RELEASE"
|
||||||
"$BUSYBOX" mount -t 9p -o trans=virtio,cache=loose,ro,msize={_9PFS_MSIZE} modules "/lib/modules/$RELEASE"
|
mount -t 9p -o trans=virtio,cache=loose,ro,msize={_9PFS_MSIZE} modules "/lib/modules/$RELEASE"
|
||||||
for module in configs rng_core virtio_rng; do
|
for module in configs rng_core virtio_rng; do
|
||||||
"$BUSYBOX" modprobe "$module"
|
modprobe "$module"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create static device nodes.
|
# Create static device nodes.
|
||||||
"$BUSYBOX" grep -v '^#' "/lib/modules/$RELEASE/modules.devname" |
|
grep -v '^#' "/lib/modules/$RELEASE/modules.devname" |
|
||||||
while read -r module name node; do
|
while read -r module name node; do
|
||||||
name="/dev/$name"
|
name="/dev/$name"
|
||||||
dev=${{node#?}}
|
dev=${{node#?}}
|
||||||
major=${{dev%%:*}}
|
major=${{dev%%:*}}
|
||||||
minor=${{dev##*:}}
|
minor=${{dev##*:}}
|
||||||
type=${{node%"${{dev}}"}}
|
type=${{node%"${{dev}}"}}
|
||||||
"$BUSYBOX" mkdir -p "$("$BUSYBOX" dirname "$name")"
|
mkdir -p "$(dirname "$name")"
|
||||||
"$BUSYBOX" mknod "$name" "$type" "$major" "$minor"
|
mknod "$name" "$type" "$major" "$minor"
|
||||||
done
|
done
|
||||||
"$BUSYBOX" ln -s /proc/self/fd /dev/fd
|
ln -s /proc/self/fd /dev/fd
|
||||||
"$BUSYBOX" ln -s /proc/self/fd/0 /dev/stdin
|
ln -s /proc/self/fd/0 /dev/stdin
|
||||||
"$BUSYBOX" ln -s /proc/self/fd/1 /dev/stdout
|
ln -s /proc/self/fd/1 /dev/stdout
|
||||||
"$BUSYBOX" ln -s /proc/self/fd/2 /dev/stderr
|
ln -s /proc/self/fd/2 /dev/stderr
|
||||||
|
|
||||||
# Configure networking.
|
# Configure networking.
|
||||||
"$BUSYBOX" hostname "$HOSTNAME"
|
cat << EOF > /etc/hosts
|
||||||
"$BUSYBOX" ip link set lo up
|
127.0.0.1 localhost
|
||||||
|
::1 localhost
|
||||||
|
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
|
||||||
|
EOF
|
||||||
|
: > /etc/resolv.conf
|
||||||
|
hostname "$HOSTNAME"
|
||||||
|
ip link set lo up
|
||||||
|
|
||||||
# Find virtio port.
|
# Find virtio port.
|
||||||
vport=
|
vport=
|
||||||
for vport_dir in /sys/class/virtio-ports/*; do
|
for vport_dir in /sys/class/virtio-ports/*; do
|
||||||
if "$BUSYBOX" [ -r "$vport_dir/name" \
|
if [ -r "$vport_dir/name" -a "$(cat "$vport_dir/name")" = "$VPORT_NAME" ]; then
|
||||||
-a "$("$BUSYBOX" cat "$vport_dir/name")" = "$VPORT_NAME" ]; then
|
|
||||||
vport="${{vport_dir#/sys/class/virtio-ports/}}"
|
vport="${{vport_dir#/sys/class/virtio-ports/}}"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if "$BUSYBOX" [ -z "$vport" ]; then
|
if [ -z "$vport" ]; then
|
||||||
"$BUSYBOX" echo "could not find virtio-port \"$VPORT_NAME\""
|
echo "could not find virtio-port \"$VPORT_NAME\""
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd {cwd}
|
cd {cwd}
|
||||||
set +e
|
set +e
|
||||||
"$BUSYBOX" sh -c {command}
|
sh -c {command}
|
||||||
rc=$?
|
rc=$?
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
"$BUSYBOX" echo "Exited with status $rc"
|
echo "Exited with status $rc"
|
||||||
"$BUSYBOX" echo "$rc" > "/dev/$vport"
|
echo "$rc" > "/dev/$vport"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -208,15 +210,11 @@ def run_in_vm(command: str, kernel_dir: Path, build_dir: Path) -> int:
|
|||||||
server_sock.bind(str(socket_path))
|
server_sock.bind(str(socket_path))
|
||||||
server_sock.listen()
|
server_sock.listen()
|
||||||
|
|
||||||
busybox = shutil.which("busybox")
|
|
||||||
if busybox is None:
|
|
||||||
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), "busybox")
|
|
||||||
init = (temp_path / "init").resolve()
|
init = (temp_path / "init").resolve()
|
||||||
with open(init, "w") as init_file:
|
with open(init, "w") as init_file:
|
||||||
init_file.write(
|
init_file.write(
|
||||||
_INIT_TEMPLATE.format(
|
_INIT_TEMPLATE.format(
|
||||||
_9PFS_MSIZE=_9PFS_MSIZE,
|
_9PFS_MSIZE=_9PFS_MSIZE,
|
||||||
busybox=shlex.quote(busybox),
|
|
||||||
python=shlex.quote(sys.executable),
|
python=shlex.quote(sys.executable),
|
||||||
cwd=shlex.quote(os.getcwd()),
|
cwd=shlex.quote(os.getcwd()),
|
||||||
command=shlex.quote(command),
|
command=shlex.quote(command),
|
||||||
@ -333,7 +331,7 @@ if __name__ == "__main__":
|
|||||||
kernel_dir = next(download_kernels(args.directory, "x86_64", (kernel,)))
|
kernel_dir = next(download_kernels(args.directory, "x86_64", (kernel,)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
command = " ".join(args.command) if args.command else '"$BUSYBOX" sh -i'
|
command = " ".join(args.command) if args.command else "sh -i"
|
||||||
sys.exit(run_in_vm(command, kernel_dir, args.directory))
|
sys.exit(run_in_vm(command, kernel_dir, args.directory))
|
||||||
except LostVMError as e:
|
except LostVMError as e:
|
||||||
print("error:", e, file=sys.stderr)
|
print("error:", e, file=sys.stderr)
|
||||||
|
Loading…
Reference in New Issue
Block a user