Re: [PATCH v2 dwarves] Refactor selftests

Alan Maguire <[email protected]>
Newsgroups org.kernel.vger.dwarves
Message-ID <[email protected]>
On 05/03/2026 00:48, Bruce McCulloch wrote:
> Hello,
> 
> Here is a v2 of the patch I sent the other day that corrects the issues
> with exit codes as well as allowing the new test_lib.sh to run with
> non-bash interpreters. I have tested this with the github CI and it
> works as expected. Please let me know if there is anything else that
> needs to change.
>

sorry, missed something last time. Running the tests I see inconsistent
output for test 4:

...
2: default_vmlinux_btf.sh
Default BTF on a system without BTF.
Soft failures: 1
Test ./default_vmlinux_btf.sh passed
---
3: flexible_arrays.sh
Flexible arrays accounting.
Test ./flexible_arrays.sh passed
---
4: gcc_true_signatures.sh
Validation of BTF encoding of true_signatures: Ok
---
5: pfunct-btf-decl-tags.sh
Check that pfunct can print btf_decl_tags read from BTF.
Test ./pfunct-btf-decl-tags.sh passed
---



I think we need to update


tests/gcc_true_signatures.sh

The github.com/acmel/dwarves repo has not yet been synced with the
official repo at

https://git.kernel.org/pub/scm/devel/pahole/pahole.git

so you may not have seen that file as a result.

If you could resync with the master branch of pahole.git and
apply the refactoring to gcc_true_signatures.sh that would
be great. Thanks!


> Bruce
> 
> ---
> 
> Simple refactoring of the testsuite with the creation of a shared
> library (tests/test_lib.sh).
> Additions include:
> * Getters to find vmlinux.
> * Functions to open tmp dirs and tmp files.
> * Functions to automatically clean tmp dirs and files on pass.
> * On fail, tmp dirs are preserved.
> * Functions to pass/fail/skip/softfail tests (works with CI).
> * Logging functions with varying verbosity levels.
> * Compatible with non-bash interpreters (for perf tests).
> * Colors.
> 
> Signed-off-by: Bruce McCulloch <[email protected]>
> ---
>  tests/btf_functions.sh        | 143 ++++++++++-----------------
>  tests/default_vmlinux_btf.sh  |  18 ++--
>  tests/flexible_arrays.sh      |  48 +++++-----
>  tests/pfunct-btf-decl-tags.sh |  34 +++----
>  tests/prettify_perf.data.sh   |  51 +++++-----
>  tests/reproducible_build.sh   |  47 ++++-----
>  tests/test_lib.sh             | 176 ++++++++++++++++++++++++++++++++++
>  tests/tests                   |  28 +++---
>  8 files changed, 345 insertions(+), 200 deletions(-)
>  create mode 100755 tests/test_lib.sh
> 
> diff --git a/tests/btf_functions.sh b/tests/btf_functions.sh
> index ee0f9ff..127640e 100755
> --- a/tests/btf_functions.sh
> +++ b/tests/btf_functions.sh
> @@ -8,45 +8,22 @@
>  # also should have been.
>  #
>  
> -outdir=
> -
> -fail()
> -{
> -	# Do not remove test dir; might be useful for analysis
> -	trap - EXIT
> -	if [[ -d "$outdir" ]]; then
> -		echo "Test data is in $outdir"
> -	fi
> -	exit 1
> -}
> -
> -cleanup()
> -{
> -	rm ${outdir}/*
> -	rmdir $outdir
> -}
> -
> -vmlinux=${vmlinux:-$1}
> -
> -if [ -z "$vmlinux" ] ; then
> -	vmlinux=$(pahole --running_kernel_vmlinux)
> -	if [ -z "$vmlinux" ] ; then
> -		echo "Please specify a vmlinux file to operate on"
> -		exit 2
> -	fi
> -fi
> +source test_lib.sh
>  
> -if [ ! -f "$vmlinux" ] ; then
> -	echo "$vmlinux file not available, please specify another"
> -	exit 2
> +vmlinux=$(get_vmlinux $1)
> +if [ $? -ne 0 ] ; then
> +	info_log "$vmlinux"
> +	test_fail
>  fi
>  
> -outdir=$(mktemp -d /tmp/btf_functions.sh.XXXXXX)
> +outdir=$(make_tmpdir)
>  
> +# Comment this out to save test data.
>  trap cleanup EXIT
>  
> -echo -n "Validation of BTF encoding of functions; this may take some time: "
> -test -n "$VERBOSE" && printf "\nEncoding..."
> +title_log "Validation of BTF encoding of functions."
> +info_log "This may take some time."
> +verbose_log "Encoding..."
>  
>  # Here we use both methods so that we test pahole --lang_exclude, that is
>  # used in the Linux kernel BTF encoding phase, and as well to make sure all
> @@ -57,7 +34,7 @@ export PAHOLE_LANG_EXCLUDE=rust
>  pahole --btf_features=default --lang_exclude=rust --btf_encode_detached=$outdir/vmlinux.btf --verbose $vmlinux |\
>  	grep "skipping BTF encoding of function" > ${outdir}/skipped_fns
>  
> -test -n "$VERBOSE" && printf "done.\n"
> +verbose_log "done."
>  
>  funcs=$(pfunct --format_path=btf $outdir/vmlinux.btf 2>/dev/null|sort)
>  
> @@ -87,8 +64,8 @@ while IFS= read -r btf ; do
>  			if [[ "$dwarf_noconst" =~ "$btf_noconst" ]]; then
>  				const_insensitive=$((const_insensitive+1))
>  			else
> -				echo "ERROR: mismatch : BTF '$btf' not found; DWARF '$dwarf'"
> -				fail
> +				error_log "ERROR: mismatch : BTF '$btf' not found; DWARF '$dwarf'"
> +				test_fail
>  			fi
>  		else
>  			inline=$((inline+1))
> @@ -98,13 +75,11 @@ while IFS= read -r btf ; do
>  	fi
>  done < $outdir/btf.funcs
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Matched $exact functions exactly."
> -	echo "Matched $inline functions with inlines."
> -	echo "Matched $const_insensitive functions with multiple const/non-const instances."
> -	echo "Ok"
> -	echo "Validation of skipped function logic..."
> -fi
> +verbose_log "Matched $exact functions exactly."
> +verbose_log "Matched $inline functions with inlines."
> +verbose_log "Matched $const_insensitive functions with multiple const/non-const instances."
> +verbose_log "Ok"
> +verbose_log "Validation of skipped function logic..."
>  
>  skipped_cnt=$(wc -l ${outdir}/skipped_fns | awk '{ print $1}')
>  
> @@ -113,16 +88,14 @@ for s in $skipped_fns ; do
>  	# Ensure the skipped function are not in BTF
>  	inbtf=$(grep " $s(" $outdir/btf.funcs)
>  	if [[ -n "$inbtf" ]]; then
> -		echo "ERROR: '${s}()' was added incorrectly to BTF: '$inbtf'"
> -		fail
> +		error_log "ERROR: '${s}()' was added incorrectly to BTF: '$inbtf'"
> +		test_fail
>  	fi
>  done
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Skipped encoding $skipped_cnt functions in BTF."
> -	echo "Ok"
> -	echo "Validating skipped functions have incompatible return values..."
> -fi
> +verbose_log "Skipped encoding $skipped_cnt functions in BTF."
> +verbose_log "Ok"
> +verbose_log "Validating skipped functions have incompatible return values..."
>  
>  return_mismatches=$(awk '/return type mismatch/ { print $1 }' $outdir/skipped_fns)
>  return_count=0
> @@ -134,17 +107,15 @@ for r in $return_mismatches ; do
>  	| uniq > ${outdir}/retvals.$r
>  	cnt=$(wc -l ${outdir}/retvals.$r | awk '{ print $1 }')
>  	if [[ $cnt -lt 2 ]]; then
> -		echo "ERROR: '${r}()' has only one return value; it should not be reported as having incompatible return values"
> -		fail
> +		error_log "ERROR: '${r}()' has only one return value; it should not be reported as having incompatible return values"
> +		test_fail
>  	fi
>  	return_count=$((return_count+1))
>  done
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Found $return_count functions with multiple incompatible return values."
> -	echo "Ok"
> -	echo "Validating skipped functions have incompatible params/counts..."
> -fi
> +verbose_log "Found $return_count functions with multiple incompatible return values."
> +verbose_log "Ok"
> +verbose_log "Validating skipped functions have incompatible params/counts..."
>  
>  param_mismatches=$(awk '/due to param / { print $1 }' $outdir/skipped_fns)
>  
> @@ -169,10 +140,8 @@ for p in $param_mismatches ; do
>  		if [[ -n "$inlined" ]]; then
>  			multiple_inline=$((multiple_inline+1))
>  		else
> -			if [[ -n "$VERBOSE" ]]; then
> -				echo "WARN: '${p}()' has only one prototype; if it was subject to late optimization, pfunct may not reflect inconsistencies pahole found."
> -				echo "Full skip message from pahole: $skipmsg"
> -			fi
> +			verbose_log "WARN: '${p}()' has only one prototype; if it was subject to late optimization, pfunct may not reflect inconsistencies pahole found."
> +			verbose_log "Full skip message from pahole: $skipmsg"
>  			warnings=$((warnings+1))
>  		fi
>  	else
> @@ -180,24 +149,22 @@ for p in $param_mismatches ; do
>  	fi
>  done
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Found $multiple instances with multiple instances with incompatible parameters."
> -	echo "Found $multiple_inline instances where inline functions were not inlined and had incompatible parameters."
> -	echo "Found $optimized instances where the function name suggests optimizations led to inconsistent parameters."
> -	echo "Found $warnings instances where pfunct did not notice inconsistencies."
> -fi
> +verbose_log "Found $multiple instances with multiple instances with incompatible parameters."
> +verbose_log "Found $multiple_inline instances where inline functions were not inlined and had incompatible parameters."
> +verbose_log "Found $optimized instances where the function name suggests optimizations led to inconsistent parameters."
> +verbose_log "Found $warnings instances where pfunct did not notice inconsistencies."
>  
>  # Some specific cases can not  be tested directly with a standard kernel.
>  # We can use the small binary in bin/ to test those cases, like packed
>  # structs passed on the stack.
>  
> -test -n "$VERBOSE" && echo -n "Validation of BTF encoding corner cases with test_bin functions; this may take some time: "
> +verbose_log "Validation of BTF encoding corner cases with test_bin functions; this may take some time: "
>  
> -test -n "$VERBOSE" && printf "\nBuilding test_bin..."
> +verbose_log "Building test_bin..."
>  tests_dir=$(realpath $(dirname $0))
>  make -C ${tests_dir}/bin >/dev/null
>  
> -test -n "$VERBOSE" && printf "\nEncoding..."
> +verbose_log "Encoding..."
>  pahole --btf_features=default --lang_exclude=rust --btf_encode_detached=$outdir/test_bin.btf \
>  	--verbose ${tests_dir}/bin/test_bin | grep "skipping BTF encoding of function" \
>  	> ${outdir}/test_bin_skipped_fns
> @@ -214,18 +181,16 @@ while IFS= read -r btf ; do
>  	# specifically tailored for tests
>  	dwarf=$(grep -F "$btf" $outdir/test_bin_dwarf.funcs)
>  	if [[ "$btf" != "$dwarf" ]]; then
> -		echo "ERROR: mismatch : BTF '$btf' not found; DWARF '$dwarf'"
> -		fail
> +		error_log "ERROR: mismatch : BTF '$btf' not found; DWARF '$dwarf'"
> +		test_fail
>  	else
>  		exact=$((exact+1))
>  	fi
>  done < $outdir/test_bin_btf.funcs
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Matched $exact functions exactly."
> -	echo "Ok"
> -	echo "Validation of skipped function logic..."
> -fi
> +verbose_log "Matched $exact functions exactly."
> +verbose_log "Ok"
> +verbose_log "Validation of skipped function logic..."
>  
>  skipped_cnt=$(wc -l ${outdir}/test_bin_skipped_fns | awk '{ print $1}')
>  
> @@ -234,16 +199,14 @@ for s in $skipped_fns ; do
>  	# Ensure the skipped function are not in BTF
>  	inbtf=$(grep " $s(" $outdir/test_bin_btf.funcs)
>  	if [[ -n "$inbtf" ]]; then
> -		echo "ERROR: '${s}()' was added incorrectly to BTF: '$inbtf'"
> -		fail
> +		error_log "ERROR: '${s}()' was added incorrectly to BTF: '$inbtf'"
> +		test_fail
>  	fi
>  done
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Skipped encoding $skipped_cnt functions in BTF."
> -	echo "Ok"
> -	echo "Validating skipped functions have uncertain parameter location..."
> -fi
> +verbose_log "Skipped encoding $skipped_cnt functions in BTF."
> +verbose_log "Ok"
> +verbose_log "Validating skipped functions have uncertain parameter location..."
>  
>  uncertain_loc=$(awk '/due to uncertain parameter location/ { print $1 }' $outdir/test_bin_skipped_fns)
>  legitimate_skip=0
> @@ -266,12 +229,10 @@ for f in $uncertain_loc ; do
>  			fi
>  		fi
>  	done
> -	echo "ERROR: '${f}()' should not have been skipped; it has no parameter with uncertain location"
> -	fail
> +	error_log "ERROR: '${f}()' should not have been skipped; it has no parameter with uncertain location"
> +	test_fail
>  done
>  
> -if [[ -n "$VERBOSE" ]]; then
> -	echo "Found ${legitimate_skip} legitimately skipped function due to uncertain loc"
> -fi
> -echo "Ok"
> -exit 0
> +verbose_log "Found ${legitimate_skip} legitimately skipped function due to uncertain loc"
> +
> +test_pass
> diff --git a/tests/default_vmlinux_btf.sh b/tests/default_vmlinux_btf.sh
> index a855ca7..496e840 100755
> --- a/tests/default_vmlinux_btf.sh
> +++ b/tests/default_vmlinux_btf.sh
> @@ -1,6 +1,7 @@
>  #!/bin/bash
> +source test_lib.sh
>  
> -echo -n "Default BTF on a system without BTF: "
> +title_log "Default BTF on a system without BTF."
>  
>  ulimit -c 0
>  
> @@ -13,8 +14,7 @@ ulimit -c 0
>  nr_lines=$(PAHOLE_VMLINUX_BTF_FILENAME=foobar pahole -F btf list_head 2>&1 | wc -l)
>  
>  if [ $nr_lines -eq 0 ] ; then
> -	echo "FAILED"
> -	exit 1
> +	test_softfail
>  fi
>  
>  # There is also the case where no debugging info is available, be it DWARF of
> @@ -22,11 +22,15 @@ fi
>  # that as well
>  #
>  nr_lines=$(PAHOLE_VMLINUX_BTF_FILENAME=foobar pahole 2>&1 | wc -l)
> +nr_lines=0
>  
>  if [ $nr_lines -eq 0 ] ; then
> -	echo "FAILED"
> -	exit 1
> +	test_softfail
>  fi
>  
> -echo "Ok"
> -exit 0
> +check_softfail
> +if [ $? -eq 2 ] ; then
> +	test_fail
> +else
> +	test_pass
> +fi
> \ No newline at end of file
> diff --git a/tests/flexible_arrays.sh b/tests/flexible_arrays.sh
> index 59fa38e..4e9e995 100755
> --- a/tests/flexible_arrays.sh
> +++ b/tests/flexible_arrays.sh
> @@ -5,22 +5,23 @@
>  #
>  # Arnaldo Carvalho de Melo <[email protected]> (C) 2024-
>  
> -vmlinux=${vmlinux:-$1}
> +source test_lib.sh
>  
> -if [ -z "$vmlinux" ] ; then
> -	vmlinux=$(pahole --running_kernel_vmlinux)
> +vmlinux=$(get_vmlinux $1)
> +if [ $? -ne 0 ] ; then
> +	info_log "$vmlinux"
> +	test_fail
>  fi
>  
> -if [ ! -f "$vmlinux" ] ; then
> -	echo "$vmlinux file not available, please specify another"
> -	exit 2
> -fi
> +outdir=$(make_tmpdir)
>  
> -pretty=$(mktemp /tmp/flexible_arrays.data.sh.XXXXXX.c)
> +# Comment this out to save test data.
> +trap cleanup EXIT
>  
> -echo -n "Flexible arrays accounting: "
> +title_log "Flexible arrays accounting."
>  
>  for struct in $(pahole -F btf --sizes --with_embedded_flexible_array $vmlinux | cut -f1) ; do
> +	pretty=$(make_tmpsrc)
>  	pahole $struct $vmlinux > $pretty
>  
>  	# We need to check for just one tab before the comment as when expanding unnamed
> @@ -48,30 +49,27 @@ for struct in $(pahole -F btf --sizes --with_embedded_flexible_array $vmlinux |
>  	[ -z "$stat_nr_flexible_arrays" ] && stat_nr_flexible_arrays=0
>  	stat_nr_embedded_flexible_arrays=$(grep "flexible array members:.*middle:" $pretty | sed -r 's/.*middle: *([[:digit:]]+).*/\1/g')
>  	[ -z "$stat_nr_embedded_flexible_arrays" ] && stat_nr_embedded_flexible_arrays=0
> -	test -n "$VERBOSE" && echo "end: $struct: $nr_flexible_arrays $stat_nr_flexible_arrays"
> -	test -n "$VERBOSE" && echo "middle: $struct: $nr_embedded_flexible_arrays $stat_nr_embedded_flexible_arrays"
> +	verbose_log "end: $struct: $nr_flexible_arrays $stat_nr_flexible_arrays"
> +	verbose_log "middle: $struct: $nr_embedded_flexible_arrays $stat_nr_embedded_flexible_arrays"
>  
>  	if [ "$nr_embedded_flexible_arrays" != "$stat_nr_embedded_flexible_arrays" ] ; then
> -		test -n "$VERBOSE" && printf "struct %s: The number of embedded flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \
> +		verbose_log "struct %s: The number of embedded flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \
>  			"$struct" "$stat_nr_embedded_flexible_arrays" "$nr_embedded_flexible_arrays"
> -		test -n "$VERBOSE" && pahole $struct $vmlinux
> -		FAILED=1
> +		verbose_log pahole $struct $vmlinux
> +		test_softfail
>  	fi
>  
>  	if [ "$nr_flexible_arrays" != "$stat_nr_flexible_arrays" ] ; then
> -		test -n "$VERBOSE" && printf "struct %s: The number of flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \
> +		verbose_log printf "struct %s: The number of flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \
>  			"$struct" "$stat_nr_flexible_arrays" "$nr_flexible_arrays"
> -		test -n "$VERBOSE" && pahole $struct $vmlinux
> -		FAILED=1
> +		verbose_log pahole $struct $vmlinux
> +		test_softfail
>  	fi
> -
> -	rm -f $pretty
>  done
>  
> -if [ -n "$FAILED" ] ; then
> -	echo "FAILED"
> -	exit 1
> +check_softfail
> +if [ $? -ne 0 ] ; then
> +	test_fail
> +else
> +	test_pass
>  fi
> -
> -echo "Ok"
> -exit 0
> diff --git a/tests/pfunct-btf-decl-tags.sh b/tests/pfunct-btf-decl-tags.sh
> index 69babef..35884b4 100755
> --- a/tests/pfunct-btf-decl-tags.sh
> +++ b/tests/pfunct-btf-decl-tags.sh
> @@ -3,21 +3,24 @@
>  
>  # Check that pfunct can print btf_decl_tags read from BTF
>  
> -tmpobj=$(mktemp /tmp/pfunct-btf-decl-tags.sh.XXXXXX.o)
> +source test_lib.sh
>  
> -cleanup()
> -{
> -	rm $tmpobj
> -}
> +outdir=$(make_tmpdir)
> +tmpobj=$(make_tmpobj)
>  
> +# Comment this out to save test data.
>  trap cleanup EXIT
>  
> -echo -n "Check that pfunct can print btf_decl_tags read from BTF: "
> +title_log "Check that pfunct can print btf_decl_tags read from BTF."
> +
> +# gcc now also supports decl tags as of gcc commit 43dcea48b8c,
> +# in upstream version 16.
> +# UPTODO: add a check here for that.
>  
>  CLANG=${CLANG:-clang}
>  if ! command -v $CLANG > /dev/null; then
> -	echo "Need clang for test $0"
> -	exit 1
> +	error_log "Need clang for test $0"
> +	test_fail
>  fi
>  
>  (cat <<EOF
> @@ -55,13 +58,12 @@ out=$(pfunct -P -F btf $tmpobj | awk "$sort_tags" | sort)
>  d=$(diff -u <(echo "$expected") <(echo "$out"))
>  
>  if [[ "$d" == "" ]]; then
> -	echo "Ok"
> -	exit 0
> +	test_pass
>  else
> -	echo "pfunct output does not match expected:"
> -	echo "$d"
> -	echo
> -	echo "Complete output:"
> -	echo "$out"
> -	exit 1
> +	error_log "pfunct output does not match expected:"
> +	info_log "$d"
> +	info_log
> +	info_log "Complete output:"
> +	info_log "$out"
> +	test_fail
>  fi
> diff --git a/tests/prettify_perf.data.sh b/tests/prettify_perf.data.sh
> index 2d16cf5..1fae951 100755
> --- a/tests/prettify_perf.data.sh
> +++ b/tests/prettify_perf.data.sh
> @@ -7,30 +7,35 @@
>  # Check if the perf binary is available, if it is from a distro, normally it
>  # will get the needed DWARF info using libddebuginfod, we'll check if the
>  # needed types are available, skipping the test and informing the reason.
> +. ./test_lib.sh
>  
> -echo -n "Pretty printing of files using DWARF type information: "
> +outdir=$(make_tmpdir)
> +
> +# Comment this out to save test data.
> +trap cleanup EXIT
> +
> +title_log "Pretty printing of files using DWARF type information."
>  
>  perf=$(which perf 2> /dev/null)
>  if [ -z "$perf" ] ; then
> -	echo "skip: No 'perf' binary available"
> -	exit 2
> +	info_log "skip: No 'perf' binary available"
> +	test_skip
>  fi
>  
>  perf_lacks_type_info() {
>  	local type_keyword=$1
>  	local type_name=$2
>  	if ! pahole -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then
> -		echo "skip: $perf doesn't have '$type_keyword $type_name' type info"
> -		return 1
> +		info_log "skip: $perf doesn't have '$type_keyword $type_name' type info"
> +		test_skip
>  	fi
> -	return 0
>  }
>  
> -perf_data=$(mktemp /tmp/prettify_perf.data.sh.XXXXXX.perf.data)
> +perf_data=$(make_tmpfile)
>  
> -perf_lacks_type_info struct perf_event_header || exit 2
> -perf_lacks_type_info enum perf_event_type || exit 2
> -perf_lacks_type_info enum perf_user_event_type || exit 2
> +perf_lacks_type_info struct perf_event_header
> +perf_lacks_type_info enum perf_event_type
> +perf_lacks_type_info enum perf_user_event_type
>  
>  $perf record --quiet -o $perf_data sleep 0.00001
>  
> @@ -46,21 +51,23 @@ check_expected_number_of_filtered_perf_record_metadata() {
>  	local nr_records=$(number_of_filtered_perf_record_metadata $metadata_record)
>  
>  	if [ "$nr_records" != "$expected_records" ] ; then
> -		echo "FAIL: expected $expected_records PERF_RECORD_$metadata_record metadata records, got $nr_records"
> -		return 1;
> +		error_log "FAIL: expected $expected_records PERF_RECORD_$metadata_record metadata records, got $nr_records"
> +		test_softfail
>  	fi
> -	return 0
>  }
>  
> -check_expected_number_of_filtered_perf_record_metadata COMM 2 || exit 1
> -check_expected_number_of_filtered_perf_record_metadata EXIT 1 || exit 1
> -check_expected_number_of_filtered_perf_record_metadata TIME_CONV 1 || exit 1
> -check_expected_number_of_filtered_perf_record_metadata THREAD_MAP 1 || exit 1
> -check_expected_number_of_filtered_perf_record_metadata CPU_MAP 1 || exit 1
> -check_expected_number_of_filtered_perf_record_metadata FINISHED_INIT 1 || exit 1
> +check_expected_number_of_filtered_perf_record_metadata COMM 2
> +check_expected_number_of_filtered_perf_record_metadata EXIT 1
> +check_expected_number_of_filtered_perf_record_metadata TIME_CONV 1
> +check_expected_number_of_filtered_perf_record_metadata THREAD_MAP 1
> +check_expected_number_of_filtered_perf_record_metadata CPU_MAP 1
> +check_expected_number_of_filtered_perf_record_metadata FINISHED_INIT 1
>  
>  # XXX write more tests that look at the events contents, not just for the presence of a known number of them
>  
> -echo "Ok"
> -
> -rm -f $perf_data
> +check_softfail
> +if [ $? -ne 0 ] ; then
> +	test_fail
> +else
> +	test_pass
> +fi
> diff --git a/tests/reproducible_build.sh b/tests/reproducible_build.sh
> index a940d93..d8c6507 100755
> --- a/tests/reproducible_build.sh
> +++ b/tests/reproducible_build.sh
> @@ -4,22 +4,22 @@
>  # Test if BTF generated serially matches reproducible parallel DWARF loading + serial BTF encoding
>  # Arnaldo Carvalho de Melo <[email protected]> (C) 2024-
>  
> -vmlinux=${vmlinux:-$1}
> +source test_lib.sh
>  
> -if [ -z "$vmlinux" ] ; then
> -	vmlinux=$(pahole --running_kernel_vmlinux)
> +vmlinux=$(get_vmlinux $1)
> +if [ $? -ne 0 ]; then
> +	info_log "$vmlinux"
> +	test_fail
>  fi
>  
> -if [ ! -f "$vmlinux" ] ; then
> -	echo "$vmlinux file not available, please specify another"
> -	exit 2
> -fi
> +outdir=$(make_tmpdir)
>  
> -outdir=$(mktemp -d /tmp/reproducible_build.sh.XXXXXX)
> +# Comment this out to save test data.
> +trap cleanup EXIT
>  
> -echo -n "Parallel reproducible DWARF Loading/Serial BTF encoding: "
> +title_log "Parallel reproducible DWARF Loading/Serial BTF encoding."
>  
> -test -n "$VERBOSE" && printf "\nserial encoding...\n"
> +verbose_log "Begin serial encoding..."
>  
>  # This will make pahole and pfunct to skip rust CUs
>  export PAHOLE_LANG_EXCLUDE=rust
> @@ -30,37 +30,32 @@ bpftool btf dump file $outdir/vmlinux.btf.serial > $outdir/bpftool.output.vmlinu
>  nr_proc=$(getconf _NPROCESSORS_ONLN)
>  
>  for threads in $(seq $nr_proc) ; do
> -	test -n "$VERBOSE" && echo $threads threads encoding
> +	verbose_log "$threads threads encoding"
>  	pahole -j$threads --btf_features=default,reproducible_build --btf_encode_detached=$outdir/vmlinux.btf.parallel.reproducible $vmlinux &
>  	pahole=$!
>  	# HACK: Wait a bit for pahole to start its threads
> -	sleep 0.3s
> +	sleep 1s
>  	# PID part to remove ps output headers
>  	nr_threads_started=$(ps -L -C pahole | grep -v PID | wc -l)
> -        ((nr_threads_started -= 1)) # main thread doesn't count, it waits to join
> +		((nr_threads_started -= 1)) # main thread doesn't count, it waits to join
>  
>  	if [ $threads != $nr_threads_started ] ; then
> -		echo "ERROR: pahole asked to start $threads encoding threads, started $nr_threads_started"
> -		exit 1;
> +		error_log "ERROR: pahole asked to start $threads encoding threads, started $nr_threads_started"
> +		test_fail
>  	fi
>  
>  	# ps -L -C pahole | grep -v PID | nl
> -	test -n "$VERBOSE" && echo $nr_threads_started threads started
> +	verbose_log "$nr_threads_started threads started"
>  	wait $pahole
>  	rm -f $outdir/bpftool.output.vmlinux.btf.parallel.reproducible
>  	bpftool btf dump file $outdir/vmlinux.btf.parallel.reproducible > $outdir/bpftool.output.vmlinux.btf.parallel.reproducible
> -	test -n "$VERBOSE" && echo "diff from serial encoding:"
> +	verbose_log "diff from serial encoding:"
>  	diff -u $outdir/bpftool.output.vmlinux.btf.serial $outdir/bpftool.output.vmlinux.btf.parallel.reproducible > $outdir/diff
>  	if [ -s $outdir/diff ] ; then
> -		echo "ERROR: BTF generated from DWARF in parallel is different from the one generated in serial!"
> -		exit 1
> +		error_log "ERROR: BTF generated from DWARF in parallel is different from the one generated in serial!"
> +		test_fail
>  	fi
> -	test -n "$VERBOSE" && echo -----------------------------
> +	verbose_log -----------------------------
>  done
>  
> -rm $outdir/*
> -rmdir $outdir
> -
> -echo "Ok"
> -
> -exit 0
> +test_pass
> diff --git a/tests/test_lib.sh b/tests/test_lib.sh
> new file mode 100755
> index 0000000..8c235da
> --- /dev/null
> +++ b/tests/test_lib.sh
> @@ -0,0 +1,176 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Copyright (c) 2026, Oracle and/or its affiliates.
> +#
> +# Common helper functions for the testsuite.
> +#
> +
> +# if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
> +# 	echo "This script is meant to be sourced. Please use 'source test_lib.sh'."
> +# 	exit 1
> +# fi
> +
> +check_color_support()
> +{
> +	if [ ! -z "$color_support" ] ; then
> +		return $color_support
> +	else
> +		if tput colors >/dev/null 2>&1; then
> +			num_colors=$(tput colors)
> +			if [ $num_colors -gt 0 ] && [ -n "$BASH_VERSION" ] ; then
> +				RED='\033[0;31m'
> +				GREEN='\033[0;32m'
> +				YELLOW='\033[0;33m'
> +				NC='\033[0m'
> +				color_support=1
> +			else
> +				RED=''
> +				GREEN=''
> +				YELLOW=''
> +				NC=''
> +				color_support=0
> +			fi
> +		else
> +			RED=''
> +			GREEN=''
> +			YELLOW=''
> +			NC=''
> +			color_support=0
> +		fi
> +	fi
> +	return $color_support
> +}
> +
> +color_print()
> +{
> +	check_color_support
> +	if [ $? -eq 1 ] ; then
> +		echo -e "$1$2${NC}"
> +	else
> +		echo $1
> +	fi
> +}
> +
> +get_vmlinux()
> +{
> +	
> +	vmlinux=${vmlinux:-$1}
> +
> +	if [ -z "$vmlinux" ] ; then
> +		vmlinux=$(pahole --running_kernel_vmlinux)
> +		if [ -z "$vmlinux" ] ; then
> +			color_print ${RED} "Please specify a vmlinux file to operate on"
> +			exit 2
> +		fi
> +	fi
> +
> +	if [ ! -f "$vmlinux" ] ; then
> +		echo ${RED} "$vmlinux file not available, please specify another"
> +		exit 2
> +	fi
> +
> +	echo $vmlinux
> +	return 0
> +}
> +
> +make_tmpdir()
> +{
> +	outdir=$(mktemp -d /tmp/$(basename "$0").XXXXXX)
> +	echo $outdir
> +	return 0
> +}
> +
> +make_tmpobj()
> +{
> +	outobj=$(mktemp $outdir/$0.obj.XXXXXX.o)
> +	echo $outobj
> +	return 0
> +}
> +
> +make_tmpsrc()
> +{
> +	outsrc=$(mktemp $outdir/$0.src.XXXXXX.c)
> +	echo $outsrc
> +	return 0
> +}
> +
> +make_tmpfile()
> +{
> +	outfile=$(mktemp $outdir/$0.data.XXXXXX)
> +	echo $outfile
> +	return 0
> +}
> +
> +info_log()
> +{
> +	printf "   "
> +	echo $1
> +}
> +
> +title_log()
> +{
> +	color_print ${YELLOW} "$1"
> +}
> +
> +verbose_log()
> +{
> +	if [[ -n "$VERBOSE" ]]; then
> +		printf "   "
> +		echo $1
> +	fi
> +}
> +
> +error_log()
> +{
> +	printf "   "
> +	color_print $RED "${1}"
> +}
> +
> +test_softfail()
> +{
> +	if [ -z "$softfail_count" ] ; then
> +		softfail_count=1
> +	else
> +		softfail_count=$((softfail_count + 1))
> +	fi
> +}
> +
> +test_fail()
> +{
> +	trap - EXIT
> +	color_print ${RED} "Test $0 failed"
> +	if [ -d "$outdir" ]; then
> +		color_print ${RED} "Test data is in $outdir"
> +	fi
> +	exit 1
> +}
> +
> +check_softfail()
> +{
> +	if [ ! -z "$softfail_count" ] ; then
> +		color_print ${RED} "Soft failures: $softfail_count"
> +		return 1
> +	else
> +		return 0
> +	fi
> +}
> +
> +test_pass()
> +{
> +	color_print ${GREEN} "Test $0 passed"
> +	exit 0
> +}
> +
> +test_skip()
> +{
> +	color_print ${YELLOW} "Skipping test ..."
> +	exit 2
> +}
> +
> +cleanup()
> +{
> +	rm ${outdir}/*
> +	rmdir $outdir
> +	return 0
> +}
> diff --git a/tests/tests b/tests/tests
> index 11921ad..b4116ce 100755
> --- a/tests/tests
> +++ b/tests/tests
> @@ -8,19 +8,21 @@ cd $tests_dir
>  let status=0
>  let nr=1
>  for test in *.sh ; do
> -	printf "%3d: " $nr
> -	./$test
> -	case $? in
> -	0)
> -		;;
> -	2)
> -		echo "skipping..."
> -		;;
> -	*)
> -		status=1
> -		;;
> -	esac
> -	let nr+=1
> +	if [ $test != "test_lib.sh" ]; then
> +		printf "%d: $test\n" $nr
> +		./$test $1
> +		case $? in
> +		0)
> +			;;
> +		2)
> +			;;
> +		*)
> +			status=1
> +			;;
> +		esac
> +		let nr+=1
> +		echo "---"
> +	fi
>  done
>  
>  cd -
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.