diff --git a/scripts/docker/build/ubuntu24/Dockerfile.cb b/scripts/docker/build/ubuntu24/Dockerfile.cb index 5c8dfd82c66d..4f88ed496c8e 100644 --- a/scripts/docker/build/ubuntu24/Dockerfile.cb +++ b/scripts/docker/build/ubuntu24/Dockerfile.cb @@ -81,12 +81,15 @@ RUN git clone --depth 1 --no-single-branch ${source} # # Install build dependencies for all branches from v4 onwards +# Debian sid fails if debian/control doesn't exist due to an issue +# in one of the included make files, so we create a blank file. # WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[4-9]*\.[0-9x]*\.x|master|${branch})$" | sort -u); \ do \ git checkout $i; \ if [ -e ./debian/control.in ] ; then \ + touch -t 202001010000 debian/control; \ debian/rules debian/control ; \ fi ; \ mk-build-deps -irt"apt-get -o Debug::pkgProblemResolver=yes $APT_OPTS" debian/control ; \ diff --git a/scripts/docker/build/ubuntu24/Dockerfile.prof b/scripts/docker/build/ubuntu24/Dockerfile.prof new file mode 100644 index 000000000000..d8e2fe31c9e2 --- /dev/null +++ b/scripts/docker/build/ubuntu24/Dockerfile.prof @@ -0,0 +1,116 @@ +# Auto generated for ubuntu24 +# from scripts/docker/m4/profiling.deb.m4 +# +# Rebuild this file with `make profiling.ubuntu24.regen` +# +ARG from=freeradius40x-build/ubuntu24 +FROM ${from} + +# Copy profiling profile scripts into the container +COPY scripts/docker/profiling/profiles/default-profiling /profile + +# +# Install profiling tools +# +# Valgrind/cachegrind +# kcachegrind + KDE/Qt libs +# gperftools +# heaptrack +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS \ + libgoogle-perftools-dev \ + google-perftools \ + valgrind \ + heaptrack \ + psmisc \ + kcachegrind \ + kio \ + libkf5iconthemes5 \ + libkf5parts5 \ + libkf5textwidgets5 \ + libqt5gui5 \ + libqt5widgets5 && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +# +# Set up Ubuntu debug symbol repository and install OS library debug symbols. +# These allow profiling tools to resolve system library calls. +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS ubuntu-dbgsym-keyring && \ + printf 'deb http://ddebs.ubuntu.com noble main restricted universe multiverse\ndeb http://ddebs.ubuntu.com noble-updates main restricted universe multiverse\n' \ + > /etc/apt/sources.list.d/ddebs.list && \ + apt-get update && \ + apt-get install -y $APT_OPTS \ + libc6-dbg \ + zlib1g-dbgsym \ + libreadline8t64-dbgsym \ + libssl3t64-dbgsym \ + libsasl2-2-dbgsym \ + libpam0g-dbgsym \ + libldap2-dbgsym \ + libtalloc2-dbgsym \ + libpcre2-8-0-dbgsym \ + libpcap0.8t64-dbgsym \ + libunbound8-dbgsym \ + libsqlite3-0-dbgsym \ + libpq5-dbgsym \ + libmariadb3-dbgsym \ + libgdbm6t64-dbgsym \ + libjson-c5-dbgsym \ + libbrotli1-dbgsym \ + libhiredis1.1.0-dbgsym \ + librdkafka1-dbgsym \ + libwbclient0-dbgsym && \ + (apt-get install -y $APT_OPTS libcurl4t64-dbgsym || true) && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +ENV DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" + +# +# Rebuild libkqueue from source with debug symbols. +# CMAKE_BUILD_TYPE must be explicitly set to prevent +# the libkqueue project from overriding the flags. +# libkqueue source currently uses "-O2 -g -DNDEBUG" by default. +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS --no-install-recommends cmake git && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* && \ + git clone --depth 1 https://github.com/mheily/libkqueue.git /tmp/libkqueue && \ + cd /tmp/libkqueue && \ + cmake -G "Unix Makefiles" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_C_FLAGS_RELWITHDEBINFO="-g3 -O1 -fno-omit-frame-pointer -DNDEBUG" \ + . && \ + make && \ + cpack -G DEB && \ + dpkg -i *.deb && \ + cd / && rm -rf /tmp/libkqueue + +# +# Install FlameGraph +# +RUN git clone --depth 1 https://github.com/brendangregg/FlameGraph /opt/flamegraph \ + && chmod +x /opt/flamegraph/*.pl /opt/flamegraph/*.sh + +# Add FlameGraph to path +ENV PATH="/opt/flamegraph:${PATH}" + +# +# Install Inferno for callgrind. Inferno is a Rust port of FlameGraph with broader format support +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS --no-install-recommends \ + cargo && \ + cargo install inferno --version 0.11.21 --locked --root /usr/local && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +EXPOSE 1812/udp 1813/udp +CMD ["/bin/sh", "-c", "while true; do sleep 60; done"] diff --git a/scripts/docker/crossbuild.mk b/scripts/docker/crossbuild.mk index 74dd1a3564cf..3b9fb4a5daaa 100644 --- a/scripts/docker/crossbuild.mk +++ b/scripts/docker/crossbuild.mk @@ -40,6 +40,14 @@ endif CB_IPREFIX:=freeradius40x-build CB_CPREFIX:=fr40x-crossbuild- +PROFILE ?= default-profiling + +# Where profiling profiles live +PROFILES_DIR:=$(CB_DIR)/profiling/profiles + +# All available profiling profiles (one sub-directory per profile) +CB_PROFILES:=$(sort $(patsubst $(PROFILES_DIR)/%,%,$(wildcard $(PROFILES_DIR)/*))) + # # This Makefile is included in-line, and not via the "boilermake" # wrapper. But it's still useful to use the same process for @@ -90,6 +98,12 @@ crossbuild.help: crossbuild.info @echo " crossbuild.IMAGE.clean - stop container and tidy up" @echo " crossbuild.IMAGE.wipe - remove Docker image" @echo "" + @echo "Profiling targets:" + @echo " crossbuild.IMAGE.profile.regen - regenerate Dockerfile.prof using default profile ($(PROFILE))" + @echo " crossbuild.IMAGE.profile.regen PROFILE= - regenerate using a specific profile" + @echo " crossbuild.IMAGE.profile.build - build profiling image using default profile ($(PROFILE))" + @echo " crossbuild.IMAGE.profile.reset - remove profiling stamp to force rebuild" + @echo "" @echo "Use 'make NOCACHE=1 ...' to disregard the Docker cache on build" # @@ -140,6 +154,20 @@ $(DD)/stamp-image.${1}: ${Q}docker build $(DOCKER_BUILD_OPTS) $(DT)/${1} -f $(DT)/${1}/Dockerfile.cb -t $(CB_IPREFIX)/${1} >$(DD)/build.${1} 2>&1 ${Q}touch $(DD)/stamp-image.${1} +# +# Build the profiling image +# +.PHONY: crossbuild.${1}.profile.build +crossbuild.${1}.profile.build: $(DD)/stamp-image.${1}-profile.build + +$(DD)/stamp-image.${1}-profile.build: $(DT)/${1}/Dockerfile.prof + ${Q}echo "BUILD ${1} (freeradius4-$(PROFILE)/${1}) > $(DD)/build.${1}-profile.build" + ${Q}docker build $(DOCKER_BUILD_OPTS) . \ + -f $(DT)/${1}/Dockerfile.prof \ + -t freeradius4-$(PROFILE)/${1} \ + >$(DD)/build.${1}-profile.build 2>&1 + ${Q}touch $(DD)/stamp-image.${1}-profile.build + # # Start up the docker container # @@ -249,6 +277,29 @@ $(DT)/${1}/Dockerfile.cb: $(DOCKER_TMPL) $(CB_DIR)/m4/crossbuild.deb.m4 $(CB_DIR ${Q}echo REGEN ${1} ${Q}m4 -I $(CB_DIR)/m4 -D D_NAME=${1} -D D_TYPE=crossbuild $$< > $$@ +# +# Regenerate Dockerfile.prof from m4 template +# +.PHONY: crossbuild.${1}.profile.regen +crossbuild.${1}.profile.regen: $(DT)/${1}/Dockerfile.prof + +$(DT)/${1}/Dockerfile.prof: $(DOCKER_TMPL) $(CB_DIR)/m4/profiling.deb.m4 + ${Q}echo REGEN ${1} + ${Q}m4 -I $(CB_DIR)/m4 \ + -D D_NAME=${1} \ + -D D_TYPE=profiling \ + -D CB_IMAGE=$(CB_IPREFIX)/${1} \ + -D PROFILE_NAME=$(PROFILE) \ + $$< > $$@ + +# +# Remove profiling stamp so next profile.build starts clean +# +.PHONY: crossbuild.${1}.profile.reset +crossbuild.${1}.profile.reset: + ${Q}echo RESET profiling ${1} + ${Q}rm -f $(DD)/stamp-image.${1}-profile.build + # # Run the build test # diff --git a/scripts/docker/m4/profiling.deb.m4 b/scripts/docker/m4/profiling.deb.m4 new file mode 100644 index 000000000000..98c272ef91b9 --- /dev/null +++ b/scripts/docker/m4/profiling.deb.m4 @@ -0,0 +1,111 @@ +ARG from=CB_IMAGE +FROM ${from} + +# Copy profiling profile scripts into the container +COPY scripts/docker/profiling/profiles/PROFILE_NAME /profile + +# +# Install profiling tools +# +# Valgrind/cachegrind +# kcachegrind + KDE/Qt libs +# gperftools +# heaptrack +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS \ + libgoogle-perftools-dev \ + google-perftools \ + valgrind \ + heaptrack \ + psmisc \ + kcachegrind \ + kio \ + libkf5iconthemes5 \ + libkf5parts5 \ + libkf5textwidgets5 \ + libqt5gui5 \ + libqt5widgets5 && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +# +# Set up Ubuntu debug symbol repository and install OS library debug symbols. +# These allow profiling tools to resolve system library calls. +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS ubuntu-dbgsym-keyring && \ + printf 'deb http://ddebs.ubuntu.com OS_CODENAME main restricted universe multiverse\ndeb http://ddebs.ubuntu.com OS_CODENAME-updates main restricted universe multiverse\n' \ + > /etc/apt/sources.list.d/ddebs.list && \ + apt-get update && \ + apt-get install -y $APT_OPTS \ + libc6-dbg \ + zlib1g-dbgsym \ + libreadline8t64-dbgsym \ + libssl3t64-dbgsym \ + libsasl2-2-dbgsym \ + libpam0g-dbgsym \ + libldap2-dbgsym \ + libtalloc2-dbgsym \ + libpcre2-8-0-dbgsym \ + libpcap0.8t64-dbgsym \ + libunbound8-dbgsym \ + libsqlite3-0-dbgsym \ + libpq5-dbgsym \ + libmariadb3-dbgsym \ + libgdbm6t64-dbgsym \ + libjson-c5-dbgsym \ + libbrotli1-dbgsym \ + libhiredis1.1.0-dbgsym \ + librdkafka1-dbgsym \ + libwbclient0-dbgsym && \ + (apt-get install -y $APT_OPTS libcurl4t64-dbgsym || true) && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +ENV DEBUGINFOD_URLS="https://debuginfod.ubuntu.com" + +# +# Rebuild libkqueue from source with debug symbols. +# CMAKE_BUILD_TYPE must be explicitly set to prevent +# the libkqueue project from overriding the flags. +# libkqueue source currently uses "-O2 -g -DNDEBUG" by default. +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS --no-install-recommends cmake git && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* && \ + git clone --depth 1 https://github.com/mheily/libkqueue.git /tmp/libkqueue && \ + cd /tmp/libkqueue && \ + cmake -G "Unix Makefiles" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_C_FLAGS_RELWITHDEBINFO="-g3 -O1 -fno-omit-frame-pointer -DNDEBUG" \ + . && \ + make && \ + cpack -G DEB && \ + dpkg -i *.deb && \ + cd / && rm -rf /tmp/libkqueue + +# +# Install FlameGraph +# +RUN git clone --depth 1 https://github.com/brendangregg/FlameGraph /opt/flamegraph \ + && chmod +x /opt/flamegraph/*.pl /opt/flamegraph/*.sh + +# Add FlameGraph to path +ENV PATH="/opt/flamegraph:${PATH}" + +# +# Install Inferno for callgrind. Inferno is a Rust port of FlameGraph with broader format support +# +RUN apt-get update && \ + apt-get install -y $APT_OPTS --no-install-recommends \ + cargo && \ + cargo install inferno --version 0.11.21 --locked --root /usr/local && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +EXPOSE 1812/udp 1813/udp +CMD ["/bin/sh", "-c", "while true; do sleep 60; done"] diff --git a/scripts/docker/profiling/profiles/default-profiling/README.md b/scripts/docker/profiling/profiles/default-profiling/README.md new file mode 100644 index 000000000000..47c4de55e390 --- /dev/null +++ b/scripts/docker/profiling/profiles/default-profiling/README.md @@ -0,0 +1,43 @@ +# Default Profiling Profile + +Default profiling profile for FreeRADIUS, using Valgrind. The scripts in this directory are copied into the profiling Docker image at build time. + +## Scripts + +| Script | Description | +|----------------|----------------------------------------------------------------| +| `configure.sh` | Configures the FreeRADIUS build with profiling options enabled | +| `start.sh` | Starts the profiling session | + +## Building the image + +The profiling image is built on top of a crossbuild image using the make targets defined in +[scripts/docker/crossbuild.mk](../../../../../crossbuild.mk). The `Dockerfile.prof` is generated +from the m4 template at [scripts/docker/m4/profiling.deb.m4](../../../../m4/profiling.deb.m4). + +```sh +# Regenerate Dockerfile.prof (if the m4 template changed) +make crossbuild.IMAGE.profile.regen + +# Build the profiling image +make crossbuild.IMAGE.profile.build +``` + +Replace `IMAGE` with the target OS, e.g. `ubuntu24`. This profile is used by default +(`PROFILE=default-profiling`). To use a different profile: + +```sh +make crossbuild.IMAGE.profile.build PROFILE= +``` + +## Usage + +Once inside the container: + +```sh +# Configure the FreeRADIUS build +./configure.sh + +# Start profiling +./start.sh +``` diff --git a/scripts/docker/profiling/profiles/default-profiling/configure.sh b/scripts/docker/profiling/profiles/default-profiling/configure.sh new file mode 100644 index 000000000000..a393fd0931a0 --- /dev/null +++ b/scripts/docker/profiling/profiles/default-profiling/configure.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +# CFLAGS used for profiling build: +# -g3 Maximum debug info for callgrind symbol resolution +# -O1 Basic optimisation for realistic hotspot costs; inlining/vectorisation/ +# unrolling disabled below so the call graph matches the source. +# -fno-omit-frame-pointer Keep frame pointers for callgrind stack walking +# -fno-inline Preserve call edges (suppresses CC_HINT(flatten) too) +# -Dalways_inline= Strip always_inline, which -fno-inline does not suppress +# -fno-plt Resolve cross-library calls via GOT rather than PLT stubs; +# PLT stubs have no DWARF info and show as ??? in callgrind. +# -fno-builtin Keep stdlib calls (memcpy, strlen, etc.) visible in the graph +# -fno-optimize-sibling-calls Suppress tail-call elimination (-O1 can still apply it) +./configure \ + --enable-developer \ + --disable-verify-ptr \ + --with-raddbdir=/etc/freeradius \ + CFLAGS="-g3 -O1 -fno-omit-frame-pointer -fno-inline -Dalways_inline= -fno-plt -fno-builtin -fno-optimize-sibling-calls" \ + LDFLAGS="-fno-omit-frame-pointer" diff --git a/scripts/docker/profiling/profiles/default-profiling/start.sh b/scripts/docker/profiling/profiles/default-profiling/start.sh new file mode 100644 index 000000000000..2cfbbcfdab8f --- /dev/null +++ b/scripts/docker/profiling/profiles/default-profiling/start.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +valgrind \ + --tool=callgrind \ + --callgrind-out-file=/profile/results/callgrind.out.%p \ + --trace-children=yes \ + --separate-threads=yes \ + --dump-instr=yes \ + --collect-jumps=yes \ + --cache-sim=yes \ + --branch-sim=yes \ + --keep-debuginfo=yes \ + --instr-atstart=yes \ + freeradius -f -l stdout -S resources.talloc_skip_cleanup=yes