Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save cfstras/86688715dade02d56c53f60e2201d5a6 to your computer and use it in GitHub Desktop.
Save cfstras/86688715dade02d56c53f60e2201d5a6 to your computer and use it in GitHub Desktop.
Installing BPF & linux perf from source

The script below is a rough outline to start using bpf tools on a WSL installation.

Sadly, WSL kernels don't ship with kernel headers, and there are also no pre-built perf packages available in the standard debian&ubuntu package repositories. They also do not seem to be built with bpf support enabled. So, in order to run both perf and any bpf tools from the great BPF Compiler Collection (bcc), we have to build all these things ourselves.

Hopefully you have a reasonably fast computer. This takes about half an hour total on a 32-core threadripper.

#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
BASE="${BASE:-$HOME}"
REQUESTED_VERSION="${KERNEL:-$(uname -r | cut -d- -f1)}"
cat <<EOF
Trying to build linux kernel version $REQUESTED_VERSION.
EOF
function get_git() {
repo="$1"
out="$2"
if [[ -d "$out" ]]; then
pushd "$out"
git fetch
popd
else
git clone "$repo" "$out"
fi
}
set -x
cd "$BASE"
sudo apt remove -y "linux-tools*" "linux-cloud-tools*" >/dev/null
sudo apt update >/dev/null
# 1. wslu: contains wslvar
# 2. block: kernel deps
# 3. block: additional deps for bcc
sudo apt install -y \
wslu \
build-essential bison flex \
libncurses-dev gawk openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf bc \
libunwind-dev libzstd-dev libcap-dev libdw-dev dwarves \
cmake zip libedit-dev \
libllvm14 llvm-14-dev libclang-14-dev python3 zlib1g-dev libfl-dev python3-setuptools \
liblzma-dev libdebuginfod-dev arping netperf iperf \
python-is-python3
# Set path so that `sudo perf` and `sudo execsnoop` etc work
cat <<EOF | sudo tee /etc/sudoers.d/kernel_tools_path
Defaults secure_path="/root/bin:/usr/share/bcc/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
EOF
# get kernel source and prepare
get_git https://github.com/microsoft/WSL2-Linux-Kernel "$BASE/WSL2-Linux-Kernel"
pushd "$BASE/WSL2-Linux-Kernel"
KERNEL_TAG="$(git tag | grep "$REQUESTED_VERSION")"
if [[ -z "$KERNEL_TAG" ]]; then
set +x
cat <<EOF
Error: no kernel source tag was found for your kernel $(uname -r)
Check the output of 'git tag' in '$BASE/WSL2-Linux-Kernel/'.
EOF
exit 1
else
echo "Found kernel version: $KERNEL_TAG"
fi
if [[ "$(git describe --tags)" != "$KERNEL_TAG" ]]; then
# New version found, rebuild clean
git checkout "$KERNEL_TAG"
sudo make clean
sudo make -C tools/perf clean
fi
# Make sure it's clean if someone aborted before
git reset --hard
cp Microsoft/config-wsl .config
make olddefconfig
KERNELVERSION="$(make kernelrelease)"
popd
# Build and install kernel
pushd "$BASE/WSL2-Linux-Kernel"
make CONFIG_IKHEADERS=m -j$(nproc) --silent
sudo make headers_install INSTALL_HDR_PATH="/usr/src/linux-headers-$KERNELVERSION" --silent
# Not sure why this isn't installed...
sudo cp -f include/linux/user.h /usr/src/linux-headers-$KERNELVERSION/include/linux/
sudo mkdir -p "/lib/modules/$KERNELVERSION"
sudo ln -sf "$(pwd)" "/lib/modules/$KERNELVERSION/build"
# Installing kernel
mkdir -p /mnt/c/wslkernel
kernel_out="$(pwd)/arch/x86/boot/bzImage"
kernel_path=/mnt/c/wslkernel
kernel_file=latest
if ! cp "$kernel_out" "$kernel_path/$kernel_file"; then
kernel_file="latest-$RANDOM"
set +x
cat <<EOF
You're already running a custom kernel, right?
Anyway, I'm going to use a different file path then: $kernel_path/$kernel_file
EOF
set -x
cp "$kernel_out" "$kernel_path/$kernel_file"
fi
# The file needs to conain escaped backslashes
echo -e "[wsl2]\nkernel = $(wslpath -m "$kernel_path/$kernel_file" | sed 's/\\/\\\\/g')" \
>"$(wslpath "$(wslvar USERPROFILE)")/.wslconfig"
popd
# Install perf from source
pushd "$BASE/WSL2-Linux-Kernel/tools/perf"
make -j$(nproc) >/dev/null
sudo make install >/dev/null
popd
# Install BCC
get_git https://github.com/iovisor/bcc.git "$BASE/bcc"
pushd "$BASE/bcc"
# New version found, rebuild clean
if [[ "$(git rev-parse origin/master)" != "$(git rev-parse HEAD)" ]]; then
rm -rf build
fi
git reset --hard origin/master
patch -p1 < "$SCRIPT_DIR/bcc-python3.patch"
popd
mkdir -p "$BASE/bcc/build"
pushd "$BASE/bcc/build"
# Note that this uses `uname -r` to find the kernel headers.
# So if you try to build a different kernel than `git tag | grep $(uname -r | cut -d- -f1)` (from above), this won't find them.
# And you'll get a useless bcc collection.
# To fix, re-run this script after rebooting WSL.
cmake ..
make -j$(nproc) >/dev/null
sudo apt remove -y bpfcc-tools libbpfcc libbpfcc-dev > /dev/null
sudo make install >/dev/null
cmake -DPYTHON_CMD=python3 ..
pushd src/python/
make -j$(nproc)
sudo make install >/dev/null
popd
popd
set +x
cat <<EOF
Done!
Press enter to shutdown your WSL instance, and run 'uname -a' after that.
(Or, press Ctrl+C to keep running your WSL instance)
You should have the new kernel version, and bpf tools like 'sudo execsnoop' or 'sudo trace', or even 'sudo memleak' available.
expected version: $KERNELVERSION
EOF
read
echo "Please wait a few seconds..."
wsl.exe --shutdown
diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py
index 6a4dcb2a..c12e9e22 100644
--- a/src/python/bcc/__init__.py
+++ b/src/python/bcc/__init__.py
@@ -401,7 +401,7 @@ class BPF(object):
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
- exe_file = os.path.join(path.encode(), bin_path)
+ exe_file = os.path.join(path, bin_path)
if is_exe(exe_file):
return exe_file
return None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment