2019-08-23 23:50:25 +00:00
|
|
|
#!/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"
|
2020-02-28 23:14:52 +00:00
|
|
|
source_abi_dir="$HOME/spdk_20_01/build/lib"
|
|
|
|
|
|
|
|
function confirm_abi_deps() {
|
|
|
|
if ! hash abidiff; then
|
|
|
|
echo "Unable to check ABI compatibility. Please install abidiff."
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ ! -d $source_abi_dir ]; then
|
|
|
|
echo "No source ABI available, failing this test."
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
for object in ls "$libdir"/libspdk_*.so; do
|
|
|
|
so_file=$(basename $object)
|
|
|
|
if [ ! -f "$source_abi_dir/$so_file" ]; then
|
|
|
|
echo "No corresponding object for $so_file in canonical directory. Skipping."
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
|
|
|
|
if ! abidiff $libdir/$so_file $source_abi_dir/$so_file --leaf-changes-only --stat > /dev/null; then
|
|
|
|
found_abi_change=false
|
|
|
|
output=$(abidiff $libdir/$so_file $source_abi_dir/$so_file --leaf-changes-only --stat) || true
|
|
|
|
new_so_maj=$(readlink $libdir/$so_file | awk -F'.so.' '{print $2}' | cut -d '.' -f1)
|
|
|
|
new_so_min=$(readlink $libdir/$so_file | awk -F'.so.' '{print $2}' | cut -d '.' -f2)
|
|
|
|
old_so_maj=$(readlink $source_abi_dir/$so_file | awk -F'.so.' '{print $2}' | cut -d '.' -f1)
|
|
|
|
old_so_min=$(readlink $source_abi_dir/$so_file | awk -F'.so.' '{print $2}' | cut -d '.' -f2)
|
|
|
|
so_name_changed=$(grep "ELF SONAME changed" <<< "$output") || so_name_changed="No"
|
|
|
|
function_summary=$(grep "functions summary" <<< "$output")
|
|
|
|
variable_summary=$(grep "variables summary" <<< "$output")
|
|
|
|
|
|
|
|
read -r _ _ _ removed_functions _ changed_functions _ added_functions _ <<< "$function_summary"
|
|
|
|
read -r _ _ _ removed_vars _ changed_vars _ added_vars _ <<< "$variable_summary"
|
|
|
|
|
|
|
|
if [ $removed_functions -ne 0 ] || [ $removed_vars -ne 0 ]; then
|
|
|
|
if [ "$new_so_maj" == "$old_so_maj" ]; then
|
|
|
|
touch $fail_file
|
|
|
|
echo "Please update the major SO version for $so_file. API functions or variables have been removed since last release."
|
|
|
|
fi
|
|
|
|
found_abi_change=true
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ $changed_functions -ne 0 ] || [ $changed_vars -ne 0 ]; then
|
|
|
|
if [ "$new_so_maj" == "$old_so_maj" ]; then
|
|
|
|
touch $fail_file
|
|
|
|
echo "Please update the major SO version for $so_file. API functions or variables have been changed since last release."
|
|
|
|
fi
|
|
|
|
found_abi_change=true
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ $added_functions -ne 0 ] || [ $added_vars -ne 0 ]; then
|
|
|
|
if [ "$new_so_min" == "$old_so_min" ] && [ "$new_so_maj" == "$old_so_maj" ] && ! $found_abi_change; then
|
|
|
|
touch $fail_file
|
|
|
|
echo "Please update the minor SO version for $so_file. API functions or variables have been added since last release."
|
|
|
|
fi
|
|
|
|
found_abi_change=true
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ "$so_name_changed" != "No" ]; then
|
|
|
|
if ! $found_abi_change; then
|
|
|
|
echo "SO name for $so_file changed without a change to abi. please revert that change."
|
|
|
|
touch $fail_file
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
}
|
2019-08-23 23:50:25 +00:00
|
|
|
|
|
|
|
# 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
|
2020-01-14 12:03:17 +00:00
|
|
|
dep_def_arr=($(grep -v "#" $libdeps_file | grep "${dep}" | cut -d "=" -f 2 | xargs))
|
2019-08-23 23:50:25 +00:00
|
|
|
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')
|
2020-01-14 12:03:17 +00:00
|
|
|
lib_make_deps=($(grep "DEPDIRS-${lib_shortname}" $libdeps_file | cut -d "=" -f 2 | xargs))
|
2019-08-23 23:50:25 +00:00
|
|
|
lib_make_deps=($(replace_defined_variables "${lib_make_deps[@]}"))
|
|
|
|
|
2019-09-06 16:36:26 +00:00
|
|
|
for ign_dep in "${IGNORED_LIBS[@]}"; do
|
|
|
|
for i in "${!lib_make_deps[@]}"; do
|
|
|
|
if [[ ${lib_make_deps[i]} == "$ign_dep" ]]; then
|
|
|
|
unset 'lib_make_deps[i]'
|
|
|
|
fi
|
|
|
|
done
|
2019-08-23 23:50:25 +00:00
|
|
|
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.
|
2019-11-25 11:21:54 +00:00
|
|
|
lib_make_deps=( $(printf "%s\n" "${lib_make_deps[@]}" | sort | grep -v "event_") )
|
2019-08-23 23:50:25 +00:00
|
|
|
# Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it.
|
2019-11-25 11:21:54 +00:00
|
|
|
dep_names=( $(printf "%s\n" "${dep_names[@]}" | sort | uniq | grep -v "env_dpdk") )
|
2019-08-23 23:50:25 +00:00
|
|
|
unset IFS
|
2019-11-25 11:21:54 +00:00
|
|
|
diff=$(echo "${dep_names[@]}" "${lib_make_deps[@]}" | tr ' ' '\n' | sort | uniq -u)
|
2019-08-23 23:50:25 +00:00
|
|
|
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 "---------------------------------------------------------------------"
|
|
|
|
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
|
|
|
|
|
2019-12-19 15:57:30 +00:00
|
|
|
$MAKE $MAKEFLAGS clean
|
2019-08-23 23:50:25 +00:00
|
|
|
./configure $config_params --with-shared
|
|
|
|
$MAKE $MAKEFLAGS
|
|
|
|
|
|
|
|
xtrace_disable
|
|
|
|
|
2020-02-28 23:14:52 +00:00
|
|
|
fail_file=$output_dir/check_so_deps_fail
|
|
|
|
|
|
|
|
rm -f $fail_file
|
|
|
|
|
|
|
|
run_test "confirm_abi_deps" confirm_abi_deps
|
|
|
|
|
2019-08-23 23:50:25 +00:00
|
|
|
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)
|
|
|
|
|
2019-09-06 16:36:26 +00:00
|
|
|
IGNORED_LIBS=()
|
|
|
|
if grep -q 'CONFIG_VHOST_INTERNAL_LIB?=n' $rootdir/mk/config.mk; then
|
|
|
|
IGNORED_LIBS+=("rte_vhost")
|
|
|
|
fi
|
|
|
|
|
scripts/rpc.py: add daemon mode
Add rpc_cmd() bash command that sends rpc command to an
rpc.py instance permanently running in background.
This makes sending RPC commands even 17 times faster.
We make use of bash coprocesses - a builtin bash feature
that allow starting background processes with stdin and
stdout connected to pipes. rpc.py will block trying to
read stdin, effectively being always "ready" to read
an RPC command.
The background rpc.py is started with a new --server flag
that's described as:
> Start listening on stdin, parse each line as a regular
> rpc.py execution and create a separate connection for each command.
> Each command's output ends with either **STATUS=0 if the
> command succeeded or **STATUS=1 if it failed.
> --server is meant to be used in conjunction with bash
> coproc, where stdin and stdout are named pipes and can be
> used as a faster way to send RPC commands.
As a part of this patch I'm attaching a sample test
that runs the following rpc commands first with the regular
rpc.py, then the new rpc_cmd() function.
```
time {
bdevs=$($rpc bdev_get_bdevs)
[ "$(jq length <<< "$bdevs")" == "0" ]
malloc=$($rpc bdev_malloc_create 8 512)
bdevs=$($rpc bdev_get_bdevs)
[ "$(jq length <<< "$bdevs")" == "1" ]
$rpc bdev_passthru_create -b "$malloc" -p Passthru0
bdevs=$($rpc bdev_get_bdevs)
[ "$(jq length <<< "$bdevs")" == "2" ]
$rpc bdev_passthru_delete Passthru0
$rpc bdev_malloc_delete $malloc
bdevs=$($rpc bdev_get_bdevs)
[ "$(jq length <<< "$bdevs")" == "0" ]
}
```
Regular rpc.py:
```
real 0m1.477s
user 0m1.289s
sys 0m0.139s
```
rpc_cmd():
```
real 0m0.085s
user 0m0.025s
sys 0m0.006s
```
autotest_common.sh will now spawn an rpc.py daemon if
it's not running yet, and it will offer rpc_cmd() function
to quickly send RPC commands. If the command is invalid or
SPDK returns with error, the bash function will return
a non-zero code and may trigger ERR trap just like a regular
rpc.py instance.
Pipes have major advantage over e.g. unix domain sockets - the pipes
will be automatically closed once the owner process exits.
This means we can create a named pipe in autotest_common.sh,
open it, then start rpc.py in background and never worry
about it again - it will be closed automatically once the
test exits. It doesn't even matter if the test is executed
manually in isolation, or as a part of the entire autotest.
(check_so_deps.sh needs to be modified not to wait for *all*
background processes to finish, but just the ones it started)
Change-Id: If0ded961b7fef3af3837b44532300dee8b5b4663
Signed-off-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Signed-off-by: Michal Berger <michalx.berger@intel.com>
Signed-off-by: Pawel Kaminski <pawelx.kaminski@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/621
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2019-06-21 07:38:47 +00:00
|
|
|
( for lib in $SPDK_LIBS; do confirm_deps $lib & done; wait )
|
2019-08-23 23:50:25 +00:00
|
|
|
|
|
|
|
$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
|