From 8228117637a5ccc2dad685b288e4405e4d93f718 Mon Sep 17 00:00:00 2001 From: Seth Howell Date: Fri, 23 Aug 2019 16:50:25 -0700 Subject: [PATCH] test: add a test to confirm shared object deps. The shared object dependencies could easily change over time. It is important that we keep this list up to date and we don't change something without updating the makefiles. This script checks each shared object file to make sure that its readelf dependencies match up with those specified in the makefile. Signed-off-by: Seth Howell Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/466179 (master) (cherry picked from commit 43d2562dc62ed1210a9cc20a5981e04c431ecb82) Change-Id: If508fb0205e85f8f5d217033194bfb5b0179d11c Signed-off-by: Seth Howell Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/466984 Tested-by: SPDK CI Jenkins Reviewed-by: Ben Walker Reviewed-by: Tomasz Zawadzki --- autobuild.sh | 4 +- test/make/check_so_deps.sh | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 3 deletions(-) create mode 100755 test/make/check_so_deps.sh diff --git a/autobuild.sh b/autobuild.sh index 5ea44cad8..52541f1e9 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -77,9 +77,7 @@ timing_enter "$make_timing_label" $MAKE $MAKEFLAGS clean if [ $SPDK_BUILD_SHARED_OBJECT -eq 1 ]; then - ./configure $config_params --with-shared - $MAKE $MAKEFLAGS - $MAKE $MAKEFLAGS clean + $rootdir/test/make/check_so_deps.sh report_test_completion "shared_object_build" fi diff --git a/test/make/check_so_deps.sh b/test/make/check_so_deps.sh new file mode 100755 index 000000000..afc8f4df2 --- /dev/null +++ b/test/make/check_so_deps.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash + +if [ "$(uname -s)" = "FreeBSD" ]; then + echo "Not testing for shared object dependencies on FreeBSD." + exit 0 +fi + +rootdir=$(readlink -f $(dirname $0)/../..) +source "$rootdir/test/common/autotest_common.sh" + +libdir="$rootdir/build/lib" +libdeps_file="$rootdir/mk/spdk.lib_deps.mk" + +# This function is needed to properly evaluate the Make variables into actual dependencies. +function replace_defined_variables() { + local arr=("$@") + local bad_values=() + local good_values=() + local new_values + for dep in "${arr[@]}"; do + if [[ $dep == *'$'* ]]; then + raw_dep=${dep/$\(/} + raw_dep=${raw_dep/\)/ } + bad_values+=("$raw_dep") + else + good_values+=("$dep") + fi + done + for dep in "${bad_values[@]}"; do + dep_def_arr=($(cat $libdeps_file | grep -v "#" | grep "${dep}" | cut -d "=" -f 2 | xargs)) + new_values=($(replace_defined_variables "${dep_def_arr[@]}")) + good_values=( "${good_values[@]}" "${new_values[@]}" ) + done + echo ${good_values[*]} +} + +function confirm_deps() { + lib=$1 + missing_syms=() + dep_names=() + found_symbol_lib="" + + #keep the space here to differentiate bdev and bdev_* + lib_shortname=$(basename $lib | sed 's,libspdk_,,g' | sed 's,\.so, ,g') + lib_make_deps=($(cat $libdeps_file | grep "DEPDIRS-${lib_shortname}" | cut -d "=" -f 2 | xargs)) + lib_make_deps=($(replace_defined_variables "${lib_make_deps[@]}")) + + for dep in ${lib_make_deps[@]}; do + if [[ $dep == *'$'* ]]; then + dep_no_dollar=$(echo $dep | sed 's,$(,,g' | sed 's,),,g') + fi + done + + symbols=$(readelf -s $lib | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort | uniq) + for symbol in $symbols; do + for deplib in $DEP_LIBS; do + if [ "$deplib" == "$lib" ]; then + continue + fi + found_symbol=$(readelf -s $deplib | grep -E "DEFAULT\s+[0-9]+\s$symbol$") || true + if [ "$found_symbol" != "" ]; then + found_symbol_lib=$(basename $deplib | sed 's,libspdk_,,g' | sed 's,\.so,,g') + break + fi + done + if [ "$found_symbol" == "" ]; then + missing_syms+=("$symbol") + else + dep_names+=("$found_symbol_lib") + fi + done + IFS=$'\n' + # Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf. + lib_make_deps=( $(printf "%s\n" ${lib_make_deps[@]} | sort | grep -v "event_") ) + # Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it. + dep_names=( $(printf "%s\n" ${dep_names[@]} | sort | uniq | grep -v "env_dpdk") ) + unset IFS + diff=$(echo ${dep_names[@]} ${lib_make_deps[@]} | tr ' ' '\n' | sort | uniq -u) + if [ "$diff" != "" ]; then + touch $fail_file + echo "there was a dependency mismatch in the library $lib_shortname" + echo "The makefile lists: '${lib_make_deps[*]}'" + echo "readelf outputs : '${dep_names[*]}'" + echo "---------------------------------------------------------------------" + elif [ ${#missing_syms[@]} -ne 0 ]; then + echo "There are still undefined symbols in the library $lib_shortname" + printf "%s\n" ${missing_syms[@]} + echo "---------------------------------------------------------------------" + fi +} + +# By removing the spdk.lib_deps.mk file from spdk.lib.mk, we ensure that we won't +# create any link dependencies. Then we can be sure we get a valid accounting of the +# symbol dependencies we have. +sed -i -e 's,include $(SPDK_ROOT_DIR)/mk/spdk.lib_deps.mk,,g' "$rootdir/mk/spdk.lib.mk" + +if [ "$SPDK_TEST_OCF" -eq 1 ]; then + config_params="$config_params --with-ocf=$rootdir/build/ocf.a" +fi + +./configure $config_params --with-shared +$MAKE $MAKEFLAGS + +xtrace_disable + +echo "---------------------------------------------------------------------" +# Exclude libspdk_env_dpdk.so from the library list. We don't link against this one so that +# users can define their own environment abstraction. However we do want to still check it +# for dependencies to avoid printing out a bunch of confusing symbols under the missing +# symbols section. +SPDK_LIBS=$(ls -1 $libdir/libspdk_*.so | grep -v libspdk_env_dpdk.so) +DEP_LIBS=$(ls -1 $libdir/libspdk_*.so) + +fail_file=$output_dir/check_so_deps_fail + +rm -f $fail_file + +for lib in $SPDK_LIBS; do + confirm_deps $lib& +done + +wait + +$MAKE $MAKEFLAGS clean +git checkout "$rootdir/mk/spdk.lib.mk" + +if [ -f $fail_file ]; then + rm -f $fail_file + echo "shared object test failed" + exit 1 +fi + +xtrace_restore