diff --git a/configure.ac b/configure.ac index 6161929de3..a09ea0477d 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ # Copyright (c) 2013-2017 Intel, Inc. All rights reserved. # Copyright (c) 2014-2017 Research Organization for Information Science # and Technology (RIST). All rights reserved. -# Copyright (c) 2016 IBM Corporation. All rights reserved. +# Copyright (c) 2016-2017 IBM Corporation. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -1407,6 +1407,7 @@ AC_CONFIG_FILES([ test/support/Makefile test/threads/Makefile test/util/Makefile + test/symbol_name/Makefile ]) m4_ifdef([project_ompi], [AC_CONFIG_FILES([test/monitoring/Makefile])]) m4_ifdef([project_ompi], [ diff --git a/test/Makefile.am b/test/Makefile.am index 7eee672d46..f3793dd1bc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -13,6 +13,7 @@ # Copyright (c) 2010 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2015-2016 Research Organization for Information Science # and Technology (RIST). All rights reserved. +# Copyright (c) 2017 IBM Corporation. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -21,7 +22,7 @@ # # support needs to be first for dependencies -SUBDIRS = support asm class threads datatype util dss +SUBDIRS = support asm class threads datatype util dss symbol_name if PROJECT_OMPI SUBDIRS += monitoring endif diff --git a/test/symbol_name/Makefile.am b/test/symbol_name/Makefile.am new file mode 100644 index 0000000000..7500c27133 --- /dev/null +++ b/test/symbol_name/Makefile.am @@ -0,0 +1,32 @@ +# +# Copyright (c) 2017 IBM Corporation. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +# Note: the Jenkins tests on LANL-distcheck and travis-ci keep +# failing when I write this Makefile.am in the "obvious" way. +# The test ends up running in $PWD +# /path/to/openmpi-gitclone/_build/test/symbol_name/ +# while files like nmcheck_prefix and nmcheck_prefix.pl are in +# /path/to/openmpi-gitclone/test/symbol_name/ +# and can be located with the env var $srcdir +# +# I tried putting nmchec_prefix.pl in check_SCRIPTS and that does +# cause a "make nmcheck_prefix.pl" step, but it then says there's +# no rule to make that target. +# +# Since I don't know what is the "correct" way to access the extra file +# nmcheck_prefix.pl, what I'm doing for now is using $srcdir to +# find it. + +TESTS = nmcheck_prefix + +EXTRA_DIST = nmcheck_prefix nmcheck_prefix.pl + +AM_TESTS_ENVIRONMENT = MYBASE='$(top_builddir)'; OMPI_LIBMPI_NAME=@OMPI_LIBMPI_NAME@; export MYBASE OMPI_LIBMPI_NAME; + +export VERBOSE=yes diff --git a/test/symbol_name/nmcheck_prefix b/test/symbol_name/nmcheck_prefix new file mode 100755 index 0000000000..23c5c4ef55 --- /dev/null +++ b/test/symbol_name/nmcheck_prefix @@ -0,0 +1,14 @@ +#!/bin/sh + +# Copyright (c) 2017 IBM Corporation. All rights reserved. + + +# if there's no perl, skip the test +perl -v > /dev/null 2>&1 +if [ $? -ne 0 ] ; then exit 77 ; fi + +# I wrote more in Makedefs.am about why I'm using ${srcdir} here. I suspect +# there's a more correct way to set up automake so the file is available, +# but nothing else has worked for me yet. + +perl ${srcdir}/nmcheck_prefix.pl diff --git a/test/symbol_name/nmcheck_prefix.pl b/test/symbol_name/nmcheck_prefix.pl new file mode 100755 index 0000000000..f912c7c556 --- /dev/null +++ b/test/symbol_name/nmcheck_prefix.pl @@ -0,0 +1,174 @@ +#!/usr/bin/env perl + +# Copyright (c) 2017 IBM Corporation. All rights reserved. + +sub main { + if (!$ENV{MYBASE}) { + print "Test expects env var MYBASE set to base dir\n"; + print "(where ompi/ opal/ orte/ test/ etc live)\n"; + print "And optionally OMPI_LIBMPI_NAME should be set\n"; + print "if MPI is configured with some name other than\n"; + print "\"mpi\" for that.\n"; + exit -1; + } + +# env var MYBASE should be the top dir where ompi/ opal/ orte/ test/ etc live. +# env var OMPI_LIBMPI_NAME should be @OMPI_LIBMPI_NAME@ from automake +# +# Most likely the libs we want to check are +# ompi/.libs/lib.so +# ompi/mpi/fortran/mpif-h/.libs/lib_mpifh.so +# ompi/mpi/fortran/use-mpi-tkr/.libs/lib_usempi.so +# orte/.libs/libopen-rte.so +# opal/.libs/libopen-pal.so +# but I hate to assume those are the locations, so I'll use 'find' and +# test whatever ".so"s are found. + + @libs = (); + $mpi = "mpi"; + if ($ENV{OMPI_LIBMPI_NAME}) { + $mpi = $ENV{OMPI_LIBMPI_NAME}; + } + for $name ( + "lib${mpi}.so", + "lib${mpi}_mpifh.so", + "lib${mpi}_usempi.so", + "libopen-rte.so", + "libopen-pal.so" ) + { + for $loc (split(/\n/, `find $ENV{MYBASE} -name $name`)) { + if ($loc !~ /openmpi-gitclone/) { + push @libs, $loc; + } + } + } + + @mca_symbols = lookup_mca_symbols(); + + print "Checking for bad symbol names in the main libs:\n"; + $isbad = 0; + for $lib (@libs) { + print "checking $lib\n"; + check_lib_for_bad_exports($lib); + } + if ($isbad) { exit(-1); } +} + +# Find libraries with names of the form libmca_coll_basic.a etc +# and presume those to be MCAs that are being built into libmpi.so etc +# rather than the usual case where it becomes mca_coll_basic.so. +# +# When name pollution occurs in an MCA .so we don't care about it. +# When it's an MCA built into libmpi.so we care a little, but aren't +# going to make this testcase fail over it. +sub lookup_mca_symbols { + my @list; + my $lib; + + @list = (); + for $lib (split(/\n/, `find $ENV{MYBASE} -name libmca_[a-zA-Z0-9_-]*\\.a`)) + { + if ($lib !~ /openmpi-gitclone/) { + print "NOTE: found static $lib\n"; + push @list, get_nm($lib, 'all'); + } + } + + return @list; +} + +sub check_lib_for_bad_exports { + my $lib = $_[0]; + my @symbols; + my $s; + + @symbols = get_nm($lib, 'all'); + + # grep to get rid of symbol prefixes that are considered acceptable, + # leaving behind anything bad: + @symbols = grep(!/^ompi_/i, @symbols); + @symbols = grep(!/^opal_/i, @symbols); + @symbols = grep(!/^orte_/i, @symbols); + @symbols = grep(!/^orted_/i, @symbols); + @symbols = grep(!/^oshmem_/i, @symbols); + @symbols = grep(!/^mpi_/i, @symbols); + @symbols = grep(!/^pmpi_/i, @symbols); + @symbols = grep(!/^pmix_/i, @symbols); + @symbols = grep(!/^pmix2x_/i, @symbols); + @symbols = grep(!/^PMI_/i, @symbols); + @symbols = grep(!/^PMI2_/i, @symbols); + @symbols = grep(!/^MPIR_/, @symbols); + @symbols = grep(!/^MPIX_/, @symbols); + @symbols = grep(!/^mpidbg_dll_locations$/, @symbols); + @symbols = grep(!/^mpimsgq_dll_locations$/, @symbols); + @symbols = grep(!/^ompit_/i, @symbols); + @symbols = grep(!/^ADIO_/i, @symbols); + @symbols = grep(!/^ADIOI_/i, @symbols); + @symbols = grep(!/^MPIO_/i, @symbols); + @symbols = grep(!/^MPIOI_/i, @symbols); + @symbols = grep(!/^MPIU_/i, @symbols); + @symbols = grep(!/^NBC_/i, @symbols); + @symbols = grep(!/^mca_/, @symbols); + + @symbols = grep(!/^_fini$/, @symbols); + @symbols = grep(!/^_init$/, @symbols); + @symbols = grep(!/^_edata$/, @symbols); + @symbols = grep(!/^_end$/, @symbols); + @symbols = grep(!/^__bss_start$/, @symbols); + @symbols = grep(!/^__malloc_initialize_hook$/, @symbols); + + # The symbols can now be split into two groups: fatal and warning. + # The warnings will be for symbols that appear to be from MCAs that + # this build has placed into a main lib, but which would normally + # be segregated into some mca_*.so + # for the fatal ones. + @warning_symbols = @fatal_symbols = (); + if (scalar(@mca_symbols) > 0) { + %whash = (); + for $s (@mca_symbols) { + $whash{$s} = 1; + } + for $s (@symbols) { + if ($whash{$s}) { + push @warning_symbols, $s; + } else { + push @fatal_symbols, $s; + } + } + } else { + @fatal_symbols = @symbols; + } + + for $s (@fatal_symbols) { + print " [error] $s\n"; + $isbad = 1; + } + for $s (@warning_symbols) { + print " [warning] $s \n"; + } +} + +# get_nm /path/to/some/libfoo.so + +sub get_nm { + my $lib = $_[0]; + my $mode = $_[1]; + my $pattern; + my $cmd; + my @tmp; + + $pattern = " [TWBCDVR] "; + if ($mode eq 'func') { $pattern = " [T] "; } + if ($mode eq 'wfunc') { $pattern = " [W] "; } + + $cmd = "nm $lib"; + $cmd = "$cmd | grep \"$pattern\""; + $cmd = "$cmd | sed -e 's/ *\$//' -e 's/.* //'"; + + @tmp = split(/\n/, qx#$cmd#); + @tmp = sort(@tmp); + + return(@tmp); +} + +main();