From a6b55450c81b90661ff45d05bb836241a9545c41 Mon Sep 17 00:00:00 2001 From: yosefe Date: Mon, 27 Oct 2014 10:40:32 +0200 Subject: [PATCH] UCS/TESTS: Add services and tests for them. * Add common services (debugging, datatypes, logging, statistics, configuration parsing, math, atomics, platform, testing). * Add a testing framework using the Google Test infrastructure with unit tests for those services. --- .gitignore | 6 +- Makefile.am | 2 +- config/m4/ax_boost_base.m4 | 260 ---- config/m4/ax_boost_thread.m4 | 171 --- config/m4/ib.m4 | 20 + config/m4/sysdep.m4 | 124 ++ config/m4/ucs.m4 | 135 ++ configure.ac | 212 +-- contrib/configure-devel | 24 + contrib/configure-prof | 25 + contrib/configure-release | 20 + contrib/dummy | 0 contrib/ucx.vim | 4 + doc/CodeStyle | 31 + src/ucs/Makefile.am | 81 ++ src/ucs/config/global_opts.c | 152 +++ src/ucs/config/global_opts.h | 87 ++ src/ucs/config/parser.c | 1133 ++++++++++++++++ src/ucs/config/parser.h | 279 ++++ src/ucs/config/types.h | 66 + src/ucs/datastruct/frag_list.c | 404 ++++++ src/ucs/datastruct/frag_list.h | 209 +++ src/ucs/datastruct/list.h | 177 +++ src/ucs/datastruct/mpool.c | 353 +++++ src/ucs/datastruct/mpool.h | 110 ++ src/ucs/datastruct/ptr_array.c | 192 +++ src/ucs/datastruct/ptr_array.h | 120 ++ src/ucs/datastruct/queue.h | 253 ++++ src/ucs/datastruct/sglib.h | 1952 ++++++++++++++++++++++++++++ src/ucs/datastruct/sglib_wrapper.h | 22 + src/ucs/debug/check.h | 22 + src/ucs/debug/debug.c | 731 +++++++++++ src/ucs/debug/debug.h | 104 ++ src/ucs/debug/instrument.c | 156 +++ src/ucs/debug/instrument.h | 121 ++ src/ucs/debug/log.c | 326 +++++ src/ucs/debug/log.h | 119 +- src/ucs/debug/memtrack.c | 493 +++++++ src/ucs/debug/memtrack.h | 254 ++++ src/ucs/gtest/main.cc | 55 + src/ucs/gtest/test.cc | 111 ++ src/ucs/gtest/test.h | 158 +++ src/ucs/gtest/test_helpers.cc | 75 ++ src/ucs/gtest/test_helpers.h | 158 +++ src/ucs/stats/client_server.c | 654 ++++++++++ src/ucs/stats/libstats.c | 66 + src/ucs/stats/libstats.h | 185 +++ src/ucs/stats/serialization.c | 505 +++++++ src/ucs/stats/stats.c | 444 +++++++ src/ucs/stats/stats.h | 107 ++ src/ucs/stats/stats_parser.c | 53 + src/ucs/stats/stats_reader.c | 266 ++++ src/ucs/sys/arch.h | 66 + src/ucs/sys/asm/asm-aarch64.h | 88 ++ src/ucs/sys/asm/asm-ppc64.h | 114 ++ src/ucs/sys/asm/asm-x86_64.h | 119 ++ src/ucs/sys/compiler.h | 137 +- src/ucs/sys/math.c | 85 ++ src/ucs/sys/math.h | 220 ++++ src/ucs/sys/preprocessor.h | 132 ++ src/ucs/sys/sys.c | 737 +++++++++++ src/ucs/sys/sys.h | 280 ++++ src/ucs/time/time.c | 49 + src/ucs/time/time.h | 142 ++ src/ucs/time/timer_wheel.c | 92 ++ src/ucs/time/timer_wheel.h | 115 ++ src/ucs/time/timerq.c | 84 ++ src/ucs/time/timerq.h | 87 ++ src/ucs/type/callback.c | 86 ++ src/ucs/type/callback.h | 78 ++ src/ucs/type/spinlock.h | 106 ++ src/ucs/type/status.c | 65 + src/ucs/type/status.h | 41 +- src/uct/api/tl.h | 4 +- src/uct/tl/tl.c | 2 +- test/gtest/Makefile.am | 77 ++ test/gtest/ucs/test_config.cc | 256 ++++ test/gtest/ucs/test_datatype.cc | 521 ++++++++ test/gtest/ucs/test_debug.cc | 116 ++ test/gtest/ucs/test_frag_list.cc | 336 +++++ test/gtest/ucs/test_instr.cc | 119 ++ test/gtest/ucs/test_math.cc | 163 +++ test/gtest/ucs/test_memtrack.cc | 119 ++ test/gtest/ucs/test_mpool.cc | 122 ++ test/gtest/ucs/test_stats.cc | 287 ++++ test/gtest/ucs/test_sys.cc | 94 ++ test/gtest/ucs/test_timer.cc | 147 +++ test/gtest/ucs/test_twheel.cc | 241 ++++ 88 files changed, 16652 insertions(+), 662 deletions(-) delete mode 100644 config/m4/ax_boost_base.m4 delete mode 100644 config/m4/ax_boost_thread.m4 create mode 100644 config/m4/ib.m4 create mode 100644 config/m4/sysdep.m4 create mode 100644 config/m4/ucs.m4 create mode 100755 contrib/configure-devel create mode 100755 contrib/configure-prof create mode 100755 contrib/configure-release delete mode 100644 contrib/dummy create mode 100644 contrib/ucx.vim create mode 100644 doc/CodeStyle create mode 100644 src/ucs/config/global_opts.c create mode 100644 src/ucs/config/global_opts.h create mode 100644 src/ucs/config/parser.c create mode 100644 src/ucs/config/parser.h create mode 100644 src/ucs/config/types.h create mode 100644 src/ucs/datastruct/frag_list.c create mode 100644 src/ucs/datastruct/frag_list.h create mode 100644 src/ucs/datastruct/list.h create mode 100644 src/ucs/datastruct/mpool.c create mode 100644 src/ucs/datastruct/mpool.h create mode 100644 src/ucs/datastruct/ptr_array.c create mode 100644 src/ucs/datastruct/ptr_array.h create mode 100644 src/ucs/datastruct/queue.h create mode 100644 src/ucs/datastruct/sglib.h create mode 100644 src/ucs/datastruct/sglib_wrapper.h create mode 100644 src/ucs/debug/check.h create mode 100644 src/ucs/debug/debug.c create mode 100644 src/ucs/debug/debug.h create mode 100644 src/ucs/debug/instrument.c create mode 100644 src/ucs/debug/instrument.h create mode 100644 src/ucs/debug/log.c create mode 100644 src/ucs/debug/memtrack.c create mode 100644 src/ucs/debug/memtrack.h create mode 100644 src/ucs/gtest/main.cc create mode 100644 src/ucs/gtest/test.cc create mode 100644 src/ucs/gtest/test.h create mode 100644 src/ucs/gtest/test_helpers.cc create mode 100644 src/ucs/gtest/test_helpers.h create mode 100644 src/ucs/stats/client_server.c create mode 100644 src/ucs/stats/libstats.c create mode 100644 src/ucs/stats/libstats.h create mode 100644 src/ucs/stats/serialization.c create mode 100644 src/ucs/stats/stats.c create mode 100644 src/ucs/stats/stats.h create mode 100644 src/ucs/stats/stats_parser.c create mode 100644 src/ucs/stats/stats_reader.c create mode 100644 src/ucs/sys/arch.h create mode 100644 src/ucs/sys/asm/asm-aarch64.h create mode 100644 src/ucs/sys/asm/asm-ppc64.h create mode 100644 src/ucs/sys/asm/asm-x86_64.h create mode 100644 src/ucs/sys/math.c create mode 100644 src/ucs/sys/math.h create mode 100644 src/ucs/sys/preprocessor.h create mode 100644 src/ucs/sys/sys.c create mode 100644 src/ucs/sys/sys.h create mode 100644 src/ucs/time/time.c create mode 100644 src/ucs/time/time.h create mode 100644 src/ucs/time/timer_wheel.c create mode 100644 src/ucs/time/timer_wheel.h create mode 100644 src/ucs/time/timerq.c create mode 100644 src/ucs/time/timerq.h create mode 100644 src/ucs/type/callback.c create mode 100644 src/ucs/type/callback.h create mode 100644 src/ucs/type/spinlock.h create mode 100644 src/ucs/type/status.c create mode 100644 test/gtest/Makefile.am create mode 100644 test/gtest/ucs/test_config.cc create mode 100644 test/gtest/ucs/test_datatype.cc create mode 100644 test/gtest/ucs/test_debug.cc create mode 100644 test/gtest/ucs/test_frag_list.cc create mode 100644 test/gtest/ucs/test_instr.cc create mode 100644 test/gtest/ucs/test_math.cc create mode 100644 test/gtest/ucs/test_memtrack.cc create mode 100644 test/gtest/ucs/test_mpool.cc create mode 100644 test/gtest/ucs/test_stats.cc create mode 100644 test/gtest/ucs/test_sys.cc create mode 100644 test/gtest/ucs/test_timer.cc create mode 100644 test/gtest/ucs/test_twheel.cc diff --git a/.gitignore b/.gitignore index dbedcdc5c6f..352d1a71012 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,6 @@ depcomp .dirstamp *.lo *.o - - - +src/ucs/ucs_stats_parser +test/gtest/gtest +build-* diff --git a/Makefile.am b/Makefile.am index 81c54349d88..6926a9de487 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ ACLOCAL_AMFLAGS = -I config/m4 -SUBDIRS = src/uct +SUBDIRS = src/uct src/ucs test/gtest EXTRA_DIST = EXTRA_DIST += m4/gtest.m4 diff --git a/config/m4/ax_boost_base.m4 b/config/m4/ax_boost_base.m4 deleted file mode 100644 index 57d14fe48d1..00000000000 --- a/config/m4/ax_boost_base.m4 +++ /dev/null @@ -1,260 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# DESCRIPTION -# -# Test for the Boost C++ libraries of a particular version (or newer) -# -# If no path to the installed boost library is given the macro searchs -# under /usr, /usr/local, /opt and /opt/local and evaluates the -# $BOOST_ROOT environment variable. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) -# -# And sets: -# -# HAVE_BOOST -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2009 Peter Adolphs -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 21 - -AC_DEFUN([AX_BOOST_BASE], -[ -AC_ARG_WITH([boost], - [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], - [use Boost library from a standard location (ARG=yes), - from the specified location (ARG=), - or disable it (ARG=no) - @<:@ARG=yes@:>@ ])], - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - ], - [want_boost="yes"]) - - -AC_ARG_WITH([boost-libdir], - AS_HELP_STRING([--with-boost-libdir=LIB_DIR], - [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), - [ - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - AC_MSG_ERROR(--with-boost-libdir expected directory name) - fi - ], - [ac_boost_lib_path=""] -) - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=ifelse([$1], ,1.20.0,$1) - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) - succeeded=no - - dnl On 64-bit systems check for system libraries in both lib64 and lib. - dnl The former is specified by FHS, but e.g. Debian does not adhere to - dnl this (as it rises problems for generic multi-arch support). - dnl The last entry in the list is chosen by default when no libraries - dnl are found, e.g. when only header-only libraries are installed! - libsubdirs="lib" - ax_arch=`uname -m` - case $ax_arch in - x86_64|ppc64|s390x|sparc64|aarch64) - libsubdirs="lib64 lib lib64" - ;; - esac - - dnl first we check the system location for boost libraries - dnl this location ist chosen if boost libraries are installed with the --layout=system option - dnl or if you install boost with RPM - if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" - for ac_boost_path_tmp in $libsubdirs; do - if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" - break - fi - done - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - break; - fi - done - fi - - dnl overwrite ld flags if we have required special directory with - dnl --with-boost-libdir parameter - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_REQUIRE([AC_PROG_CXX]) - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - - - - dnl if we found no boost with system layout we search for boost libraries - dnl built and installed without the --layout=system option or for a staged(not installed) version - if test "x$succeeded" != "xyes"; then - _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then - for libsubdir in $libsubdirs ; do - if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$best_path/$libsubdir" - fi - fi - - if test "x$BOOST_ROOT" != "x"; then - for libsubdir in $libsubdirs ; do - if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then - AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - fi - - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) - else - AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) - fi - # execute ACTION-IF-NOT-FOUND (if present): - ifelse([$3], , :, [$3]) - else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) - AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) - # execute ACTION-IF-FOUND (if present): - ifelse([$2], , :, [$2]) - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - -]) diff --git a/config/m4/ax_boost_thread.m4 b/config/m4/ax_boost_thread.m4 deleted file mode 100644 index 9f6f9bea371..00000000000 --- a/config/m4/ax_boost_thread.m4 +++ /dev/null @@ -1,171 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_THREAD -# -# DESCRIPTION -# -# Test for Thread library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_THREAD_LIB) -# -# And sets: -# -# HAVE_BOOST_THREAD -# -# LICENSE -# -# Copyright (c) 2009 Thomas Porschberg -# Copyright (c) 2009 Michael Tindal -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 23 - -AC_DEFUN([AX_BOOST_THREAD_CHECK_LIB], -[ - ax_lib=$1 - - SAVED_LIBS=$LIBS - LIBS="$LIBS -l$1 -lboost_system" - AC_LANG_PUSH([C++]) - AC_LINK_IFELSE( - [AC_LANG_PROGRAM([#include ], - [boost::this_thread::yield();])], - [$2], - [$3]) - AC_LANG_POP([C++]) - LIBS="$SAVED_LIBS" -]) - -AC_DEFUN([AX_BOOST_THREAD], -[ - AC_ARG_WITH([boost-thread], - AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], - [use the Thread library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-thread=boost_thread-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_thread_lib="" - else - want_boost="yes" - ax_boost_user_thread_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::Thread library is available, - ax_cv_boost_thread, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - if test "x$host_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$host_os" = "xmingw32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::thread_group thrds; - return 0;]])], - ax_cv_boost_thread=yes, ax_cv_boost_thread=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$host_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$host_os" = "xmingw32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi - - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - case "x$host_os" in - *bsd* ) - LDFLAGS="-pthread $LDFLAGS" - break; - ;; - esac - if test "x$ax_boost_user_thread_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_thread*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.a*$;\1;'`; do - ax_lib=${libextension} - AX_BOOST_THREAD_CHECK_LIB($ax_lib, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - for libextension in `ls $BOOSTLIBDIR/${target}/libboost_thread*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.a*$;\1;'`; do - ax_lib=${libextension} - AX_BOOST_THREAD_CHECK_LIB($ax_lib, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - if test "x$link_thread" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_thread*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.a*$;\1;'` ; do - ax_lib=${libextension} - AX_BOOST_THREAD_CHECK_LIB($ax_lib, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do - AX_BOOST_THREAD_CHECK_LIB($ax_lib, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_thread" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - else - case "x$host_os" in - *bsd* ) - BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - break; - ;; - esac - - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/config/m4/ib.m4 b/config/m4/ib.m4 new file mode 100644 index 00000000000..1014316ad88 --- /dev/null +++ b/config/m4/ib.m4 @@ -0,0 +1,20 @@ +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + + +# +# RC Support +# +AC_ARG_WITH([rc], + [AC_HELP_STRING([--with-rc], [Compile with RC support])], + [], + [with_rc=yes]) +AM_CONDITIONAL([HAVE_TL_RC], [test "x$with_rc" != xno]) +AS_IF([test "x$with_rc" != xno], + [AC_DEFINE([HAVE_TL_RC], 1, [RC transport support]) + transports="${transports},rc"]) + diff --git a/config/m4/sysdep.m4 b/config/m4/sysdep.m4 new file mode 100644 index 00000000000..6379455c78e --- /dev/null +++ b/config/m4/sysdep.m4 @@ -0,0 +1,124 @@ +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + + +# +# SystemV shared memory +# +#IPC_INFO +AC_CHECK_LIB([rt], [shm_open], [], AC_MSG_ERROR([librt not found])) + +# +# Extended string functions +# +AC_CHECK_DECLS([asprintf, strdupa, basename], [], + AC_MSG_ERROR([GNU string extensions not found]), + [#define _GNU_SOURCE 1 + #include + #include ]) + + +# +# CPU-sets +# +AC_CHECK_DECLS([CPU_ZERO, CPU_ISSET], [], + AC_MSG_ERROR([CPU_ZERO/CPU_ISSET not found]), + [#define _GNU_SOURCE 1 + #include ]) + + +# +# pthread +# +AC_SEARCH_LIBS(pthread_create, pthread) + + +# +# Route file descriptor signal to specific thread +# +AC_CHECK_DECLS([F_SETOWN_EX], [], [], [#define _GNU_SOURCE 1 +#include ]) + + +# +# PowerPC query for TB frequency +# +AC_CHECK_DECLS([__ppc_get_timebase_freq], [], [], [#include ]) +AC_CHECK_HEADERS([sys/platform/ppc.h]) + + +# +# Zlib +# +AC_CHECK_LIB(z, compress2,, AC_MSG_WARN([zlib library not found])) + + +# +# Google Testing framework +# +GTEST_LIB_CHECK([1.5.0], [true], [true]) + + +# +# Boost C++ library (if we're using gtest) +# +if test "x$HAVE_GTEST" = "xyes"; then + + AC_LANG_PUSH([C++]) + AC_MSG_CHECKING([for boost]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]] + [[ + #if (BOOST_VERSION < 103800) + # error Failed + #endif + ]])], + AC_MSG_RESULT([yes]), + AC_MSG_ERROR([Please install boost development libraries version 1.38 of above])) + + AC_CHECK_DECLS([BOOST_FOREACH], [], AC_MSG_ERROR([BOOST_FOREACH not supported]), + [#include ]) + AC_LANG_POP +fi + + +# +# Zlib +# +AC_ARG_WITH([zlib], + [AC_HELP_STRING([--with-zlib=DIR], + [Specify path to external zlib library.])], + [if test "$withval" != no; then + if test "$withval" != yes; then + ZLIB_DIR=$withval + fi + fi]) +if test -n "$ZLIB_DIR"; then + LDFLAGS="$LDFLAGS -L$ZLIB_DIR" +fi + + +# +# Valgrind support +# +AC_ARG_WITH([valgrind], + AC_HELP_STRING([--with-valgrind], + [Enable Valgrind annotations (small runtime overhead, default NO)]), + [], + [with_valgrind=no] +) +AS_IF([test "x$with_valgrind" == xno], + [AC_DEFINE([NVALGRIND], 1, [Define to 1 to disable Valgrind annotations.]) + ], + [AC_CHECK_HEADER([valgrind/memcheck.h], [], + [AC_MSG_ERROR([Valgrind memcheck support requested, but not found, install valgrind-devel rpm.])]) + if test -d $with_valgrind; then + CPPFLAGS="$CPPFLAGS -I$with_valgrind/include" + fi + ] +) + diff --git a/config/m4/ucs.m4 b/config/m4/ucs.m4 new file mode 100644 index 00000000000..756038780cc --- /dev/null +++ b/config/m4/ucs.m4 @@ -0,0 +1,135 @@ +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + + +# +# Internal instrumentation support. +# This option may affect perofrmance so it is off by default. +# +AC_ARG_ENABLE([instrumentation], + AS_HELP_STRING([--enable-instrumentation], [Enable instrumentation support, default: NO]), + [], + [enable_instrumentation=no]) + +AS_IF([test "x$enable_instrumentation" == xyes], + [AC_DEFINE([HAVE_INSTRUMENTATION], [1], [Enable instrumentation])] + [:] +) + + +# +# Detailed backtrace with debug information. +# This option requires binutils-devel package. +# +AC_ARG_ENABLE([backtrace-detail], + AS_HELP_STRING([--disable-backtrace-detail], [Disable detailed backtrace support, default: NO]), + [], + [enable_backtrace_detail=yes]) + +AS_IF([test "x$enable_backtrace_detail" == xyes], + [ + BT=1 + AC_CHECK_HEADER([bfd.h], [], [AC_MSG_WARN([binutils headers not found])]; BT=0) + AC_CHECK_HEADERS([libiberty.h libiberty/libiberty.h], [], [], + [#define HAVE_DECL_BASENAME 1]) + if test "x$ac_cv_header_libiberty_h" == "x" && test "x$ac_cv_header_libiberty_libiberty_h" == "x"; then + AC_MSG_WARN([binutils headers not found]); BT=0 + fi + AC_CHECK_LIB(bfd, bfd_init, LIBS="$LIBS -lbfd", [AC_MSG_WARN([bfd library not found])];BT=0) + AC_CHECK_LIB(iberty, xstrerror, LIBS="$LIBS -liberty", [AC_MSG_WARN([iberty library not found])];BT=0) + AC_CHECK_LIB(dl, dlopen, LIBS="$LIBS -ldl", [AC_MSG_WARN([dl library not found])];BT=0) + AC_CHECK_LIB(intl, main, LIBS="$LIBS -lintl", [AC_MSG_WARN([intl library not found])]) + AC_CHECK_TYPES([struct dl_phdr_info], [], [AC_MSG_WARN([struct dl_phdr_info not defined])];BT=0, + [#define _GNU_SOURCE 1 + #include ]) + if test "x$BT" == "x1"; then + AC_DEFINE([HAVE_DETAILED_BACKTRACE], 1, [Enable detailed backtrace]) + else + AC_MSG_WARN([detailed backtrace is not supported]) + fi + ] +) + + +# +# Enable statistics and counters +# +AC_ARG_ENABLE([stats], + AS_HELP_STRING([--enable-stats], + [Enable statistics, useful for profiling, default: NO]), + [], + [enable_stats=no]) + +AS_IF([test "x$enable_stats" == xyes], + [AS_MESSAGE([enabling statistics]) + AC_DEFINE([ENABLE_STATS], [1], [Enable statistics]) + HAVE_STATS=yes], + [:] + ) +AM_CONDITIONAL([HAVE_STATS],[test "x$HAVE_STATS" = "xyes"]) + + +# +# Enable tuning params at runtime +# +AC_ARG_ENABLE([tuning], + AS_HELP_STRING([--enable-tuning], + [Enable parameter tuning in run-time, default: NO]), + [], + [enable_tuning=no]) + +AS_IF([test "x$enable_tuning" == xyes], + [AS_MESSAGE([enabling tuning]) + AC_DEFINE([ENABLE_TUNING], [1], [Enable tuning]) + HAVE_TUNING=yes], + [:] + ) +AM_CONDITIONAL([HAVE_TUNING],[test "x$HAVE_TUNING" = "xyes"]) + + +# +# Enable memory tracking +# +AC_ARG_ENABLE([memtrack], + AS_HELP_STRING([--enable-memtrack], + [Enable memory tracking, useful for profiling, default: NO]), + [], + [enable_memtrack=no]) + +AS_IF([test "x$enable_memtrack" == xyes], + [AS_MESSAGE([enabling memory tracking]) + AC_DEFINE([ENABLE_MEMTRACK], [1], [Enable memory tracking]) + HAVE_MEMTRACK=yes], + [:] + ) +AM_CONDITIONAL([HAVE_MEMTRACK],[test "x$HAVE_MEMTRACK" = "xyes"]) + + +# +# Disable logging levels below INFO +# +AC_ARG_ENABLE([logging], + AS_HELP_STRING([--enable-logging], + [Enable debug logging, default: YES]) + ) + +AS_IF([test "x$enable_logging" != xno], + [AC_DEFINE([UCS_MAX_LOG_LEVEL], [UCS_LOG_LEVEL_TRACE_POLL], [Highest log level])], + [AC_DEFINE([UCS_MAX_LOG_LEVEL], [UCS_LOG_LEVEL_INFO], [Highest log level])] + ) + + +# +# Disable assertions +# +AC_ARG_ENABLE([assertions], + AS_HELP_STRING([--disable-assertions], + [Disable code assertions, default: NO]), + [], + [AC_DEFINE([ENABLE_ASSERT], [1], [Enable assertions])]) + + diff --git a/configure.ac b/configure.ac index cc56bd830d5..db09d45223b 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ # # $COPYRIGHT$ # $HEADER$ +# define([scm_r], esyscmd([sh -c "git rev-parse --short=7 HEAD"])) define([ucx_ver_major], 0) @@ -51,12 +52,7 @@ AC_PROG_INSTALL AC_PROG_LIBTOOL AC_HEADER_STDC LT_LIB_M -AC_CHECK_LIB([rt], [shm_open], [], AC_MSG_ERROR([librt not found])) -AC_SEARCH_LIBS(pthread_create, pthread) -AC_CHECK_DECLS([F_SETOWN_EX], [], [], [#define _GNU_SOURCE 1 -#include ]) -AC_CHECK_DECLS([__ppc_get_timebase_freq], [], [], [#include ]) -AC_CHECK_HEADERS([sys/platform/ppc.h]) + PKG_PROG_PKG_CONFIG PKG_CONFIG="pkg-config" @@ -87,39 +83,6 @@ CFLAGS="-g -Wall -Werror $UCX_CFLAGS" CXXFLAGS="$CFLAGS" -# -# Zlib -# -AC_ARG_WITH([zlib], - [AC_HELP_STRING([--with-zlib=DIR], - [Specify path to external zlib library.])], - [if test "$withval" != no; then - if test "$withval" != yes; then - ZLIB_DIR=$withval - fi - fi]) -if test -n "$ZLIB_DIR"; then - LDFLAGS="$LDFLAGS -L$ZLIB_DIR" -fi - - -# -# RC Support -# -AC_ARG_WITH([rc], - [AC_HELP_STRING([--with-rc], [Compile with RC support])], - [], - [with_rc=yes]) -AM_CONDITIONAL([HAVE_TL_RC], [test "x$with_rc" != xno]) -AS_IF([test "x$with_rc" != xno], - [AC_DEFINE([HAVE_TL_RC], 1, [RC transport support]) - transports="${transports},rc"]) - - -# Print which transports are supported -AC_MSG_NOTICE([Supported transports: $transports]) - - # # Debug mode # @@ -133,90 +96,11 @@ AS_IF([test "x$enable_debug" == xyes], # -# Valgrind support -# -AC_ARG_WITH([valgrind], - AC_HELP_STRING([--with-valgrind], - [Enable Valgrind annotations (small runtime overhead, default NO)]), - [], - [with_valgrind=no] -) -AS_IF([test "x$with_valgrind" == xno], - [AC_DEFINE([NVALGRIND], 1, [Define to 1 to disable Valgrind annotations.]) - ], - [AC_CHECK_HEADER([valgrind/memcheck.h], [], - [AC_MSG_ERROR([Valgrind memcheck support requested, but not found, install valgrind-devel rpm.])]) - if test -d $with_valgrind; then - CPPFLAGS="$CPPFLAGS -I$with_valgrind/include" - fi - ] -) - - -# -# Zlib +# Configure modules # -AC_CHECK_LIB(z, compress2,, AC_MSG_WARN([zlib library not found])) - - -# -# Google Testing framework -# -GTEST_LIB_CHECK([1.5.0], [true], [true]) - - -# -# Boost C++ library (if we're using gtest) -# -if test "x$HAVE_GTEST" = "xyes"; then - AX_BOOST_BASE([1.38], [], - [AC_MSG_ERROR([Please install boost development libraries])]) - AX_BOOST_THREAD -fi - - -# -# Internal instrumentation support. -# This option may affect perofrmance so it is off by default. -# -AC_ARG_ENABLE([instrumentation], - AS_HELP_STRING([--enable-instrumentation], [Enable instrumentation support, default: NO]), - [], - [enable_instrumentation=no]) - -AS_IF([test "x$enable_instrumentation" == xyes], - [AC_DEFINE([HAVE_INSTRUMENTATION], [1], [Enable instrumentation])] - [:] -) - - -# -# Detailed backtrace with debug information. -# This option requires binutils-devel package. -# -AC_ARG_ENABLE([backtrace-detail], - AS_HELP_STRING([--disable-backtrace-detail], [Disable detailed backtrace support, default: NO]), - [], - [enable_backtrace_detail=yes]) - -AS_IF([test "x$enable_backtrace_detail" == xyes], - [ - BT=1 - AC_CHECK_HEADER([bfd.h], [], [AC_MSG_WARN([binutils headers not found])]; BT=0) - AC_CHECK_HEADERS([libiberty.h libiberty/libiberty.h], [], [], - [#define HAVE_DECL_BASENAME 1]) - if test "x$ac_cv_header_libiberty_h" == "x" && test "x$ac_cv_header_libiberty_libiberty_h" == "x"; then - AC_MSG_WARN([binutils headers not found]); BT=0 - fi - AC_CHECK_LIB(bfd, bfd_init, LIBS="$LIBS -lbfd", [AC_MSG_WARN([bfd library not found])];BT=0) - AC_CHECK_LIB(iberty, xstrerror, LIBS="$LIBS -liberty", [AC_MSG_WARN([iberty library not found])];BT=0) - AC_CHECK_LIB(dl, dlopen, LIBS="$LIBS -ldl", [AC_MSG_WARN([dl library not found])];BT=0) - AC_CHECK_LIB(intl, main, LIBS="$LIBS -lintl", [AC_MSG_WARN([intl library not found])]) - if test "x$BT" == "x1"; then - AC_DEFINE([HAVE_DETAILED_BACKTRACE], 1, [Enable detailed backtraces]) - fi - ] -) +m4_include([config/m4/sysdep.m4]) +m4_include([config/m4/ucs.m4]) +m4_include([config/m4/ib.m4]) # @@ -237,60 +121,6 @@ AS_IF([test "x$enable_frame_pointer" == xyes], ) -# -# Enable statistics and counters -# -AC_ARG_ENABLE([stats], - AS_HELP_STRING([--enable-stats], - [Enable statistics, useful for profiling, default: NO]), - [], - [enable_stats=no]) - -AS_IF([test "x$enable_stats" == xyes], - [AS_MESSAGE([enabling statistics]) - AC_DEFINE([ENABLE_STATS], [1], [Enable statistics]) - HAVE_STATS=yes], - [:] - ) -AM_CONDITIONAL([HAVE_STATS],[test "x$HAVE_STATS" = "xyes"]) - - -# -# Enable tuning params at runtime -# -AC_ARG_ENABLE([tuning], - AS_HELP_STRING([--enable-tuning], - [Enable parameter tuning in run-time, default: NO]), - [], - [enable_tuning=no]) - -AS_IF([test "x$enable_tuning" == xyes], - [AS_MESSAGE([enabling tuning]) - AC_DEFINE([ENABLE_TUNING], [1], [Enable tuning]) - HAVE_TUNING=yes], - [:] - ) -AM_CONDITIONAL([HAVE_TUNING],[test "x$HAVE_TUNING" = "xyes"]) - - -# -# Enable memory tracking -# -AC_ARG_ENABLE([memtrack], - AS_HELP_STRING([--enable-memtrack], - [Enable memory tracking, useful for profiling, default: NO]), - [], - [enable_memtrack=no]) - -AS_IF([test "x$enable_memtrack" == xyes], - [AS_MESSAGE([enabling memory tracking]) - AC_DEFINE([ENABLE_MEMTRACK], [1], [Enable memory tracking]) - HAVE_MEMTRACK=yes], - [:] - ) -AM_CONDITIONAL([HAVE_MEMTRACK],[test "x$HAVE_MEMTRACK" = "xyes"]) - - # # Enable fault injection code # @@ -307,30 +137,6 @@ AS_IF([test "x$enable_fault_injection" == xyes], ) -# -# Disable logging levels below INFO -# -AC_ARG_ENABLE([logging], - AS_HELP_STRING([--enable-logging], - [Enable debug logging, default: YES]) - ) - -AS_IF([test "x$enable_logging" != xno], - [AC_DEFINE([UCX_MAX_LOG_LEVEL], [UCX_LOG_LEVEL_TRACE_POLL], [Highest log level])], - [AC_DEFINE([UCX_MAX_LOG_LEVEL], [UCX_LOG_LEVEL_INFO], [Highest log level])] - ) - - -# -# Disable assertions -# -AC_ARG_ENABLE([assertions], - AS_HELP_STRING([--disable-assertions], - [Disable code assertions, default: NO]), - [], - [AC_DEFINE([ENABLE_ASSERT], [1], [Enable assertions])]) - - # # Disable checking user parameters # @@ -351,6 +157,11 @@ AC_ARG_ENABLE([debug-data], [AC_DEFINE([ENABLE_DEBUG_DATA], [0])]) +# +# Print which transports are built +# +AC_MSG_NOTICE([Supported transports: $transports]) + # # Final output # @@ -359,6 +170,7 @@ AC_CONFIG_FILES([ src/ucs/Makefile src/uct/Makefile src/uct/api/version.h + test/gtest/Makefile ]) AC_OUTPUT diff --git a/contrib/configure-devel b/contrib/configure-devel new file mode 100755 index 00000000000..bdb446df918 --- /dev/null +++ b/contrib/configure-devel @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + +# +# UCX build for development. +# Full logging and an runtime checks. +# + +basedir=$(cd $(dirname $0) && pwd) +$basedir/../configure \ + --enable-gtest \ + --with-valgrind \ + --enable-instrumentation \ + --enable-frame-pointer \ + --enable-stats \ + --enable-memtrack \ + --enable-fault-injection \ + --enable-debug-data \ + "$@" diff --git a/contrib/configure-prof b/contrib/configure-prof new file mode 100755 index 00000000000..641691acd18 --- /dev/null +++ b/contrib/configure-prof @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + +# +# UCX build for profiling purposes. +# Some extra code to ease performance debugging. +# + +basedir=$(cd $(dirname $0) && pwd) +$basedir/../configure \ + --disable-logging \ + --disable-debug \ + --disable-assertions \ + --disable-params-check \ + --enable-backtrace-detail \ + --enable-instrumentation \ + --enable-frame-pointer \ + --enable-stats \ + --enable-memtrack \ + "$@" diff --git a/contrib/configure-release b/contrib/configure-release new file mode 100755 index 00000000000..ef5b44e0ea7 --- /dev/null +++ b/contrib/configure-release @@ -0,0 +1,20 @@ +#!/bin/sh +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + +# +# UCX build for maximal performance. +# No extra debugging or profiling code. +# + +basedir=$(cd $(dirname $0) && pwd) +$basedir/../configure \ + --disable-logging \ + --disable-debug \ + --disable-assertions \ + --disable-params-check \ + "$@" diff --git a/contrib/dummy b/contrib/dummy deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/contrib/ucx.vim b/contrib/ucx.vim new file mode 100644 index 00000000000..5cf9ff279cb --- /dev/null +++ b/contrib/ucx.vim @@ -0,0 +1,4 @@ +set tabstop=4 +set shiftwidth=4 +set expandtab +set colorcolumn=72,80 \ No newline at end of file diff --git a/doc/CodeStyle b/doc/CodeStyle new file mode 100644 index 00000000000..7334ec09f5d --- /dev/null +++ b/doc/CodeStyle @@ -0,0 +1,31 @@ + + +* Style + - 4 spaces, no tabs + - up to 80 columns + - names must begin with ucp/uct/ucs + - single space around operators + - indent function arguments on column + - no spaces in the end-of-line + +* C++ + - used only for unit testing + - lower-case class names (same as stl/boost) + +* Include order: + 1. config.h + 2. specific internal header + 3. ucx headers + 4. system headers + +* Doxygen + - all interface H/C files should have doxygen documentation. + +* Error handling + - all internal error codes must be ucs_status_t + - a function which returns error should print a log message + - the function which prints the log message is the first one which decides which + error it is. If a functions returns an error because it's callee returned + erroneous ucs_status_t, it does not have to print a log message. + +* Logging \ No newline at end of file diff --git a/src/ucs/Makefile.am b/src/ucs/Makefile.am index 04e7ac8f8f8..8049ddebdbb 100644 --- a/src/ucs/Makefile.am +++ b/src/ucs/Makefile.am @@ -5,3 +5,84 @@ # $HEADER$ # +AUTOMAKE_OPTIONS = nostdinc # avoid collision with built-in debug.h +AM_CPPFLAGS = -I$(top_builddir) -I$(abs_top_srcdir)/src +lib_LTLIBRARIES = libucs.la libucstest.la +bin_PROGRAMS = ucs_stats_parser + +libucs_la_LDFLAGS = -ldl -version-info $(SOVERSION) +libucs_la_LIBADD = $(LIBM) +libucs_ladir = $(includedir) + +libucstest_la_LDFLAGS = -ldl -version-info $(SOVERSION) +libucstest_la_LIBADD = libucs.la +libucstest_ladir = $(includedir) + +noinst_HEADERS = \ + config/global_opts.h \ + config/parser.h \ + config/types.h \ + datastruct/frag_list.h \ + datastruct/list.h \ + datastruct/mpool.h \ + datastruct/ptr_array.h \ + datastruct/queue.h \ + datastruct/sglib.h \ + datastruct/sglib_wrapper.h \ + debug/log.h \ + debug/check.h \ + debug/debug.h \ + debug/instrument.h \ + debug/memtrack.h \ + gtest/test_helpers.h \ + gtest/test.h \ + stats/stats.h \ + stats/libstats.h \ + sys/asm/asm-aarch64.h \ + sys/asm/asm-ppc64.h \ + sys/asm/asm-x86_64.h \ + sys/arch.h \ + sys/compiler.h \ + sys/math.h \ + sys/preprocessor.h \ + sys/sys.h \ + time/time.h \ + time/timer_wheel.h \ + time/timerq.h \ + type/status.h \ + type/callback.h \ + type/spinlock.h + +libucs_la_SOURCES = \ + config/global_opts.c \ + config/parser.c \ + datastruct/frag_list.c \ + datastruct/mpool.c \ + datastruct/ptr_array.c \ + debug/debug.c \ + debug/instrument.c \ + debug/log.c \ + debug/memtrack.c \ + stats/client_server.c \ + stats/serialization.c \ + stats/stats.c \ + stats/libstats.c \ + sys/math.c \ + sys/sys.c \ + time/time.c \ + time/timer_wheel.c \ + time/timerq.c \ + type/callback.c \ + type/status.c + +libucstest_la_SOURCES = \ + gtest/main.cc \ + gtest/test_helpers.cc \ + gtest/test.cc + +ucs_stats_parser_LDADD = libucs.la +ucs_stats_parser_SOURCES = stats/stats_parser.c + + +#TODO stats/stats_dump.c +#TODO stats/stats_reader.c diff --git a/src/ucs/config/global_opts.c b/src/ucs/config/global_opts.c new file mode 100644 index 00000000000..255adc95448 --- /dev/null +++ b/src/ucs/config/global_opts.c @@ -0,0 +1,152 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "global_opts.h" + +#include +#include + + +ucs_global_opts_t ucs_global_opts = { + .log_level = UCS_LOG_LEVEL_WARN, + .log_file = "", + .log_buffer_size = 1024, + .log_data_size = 0, + .handle_errors = UCS_HANDLE_ERROR_BACKTRACE, + .error_signals = { NULL, 0 }, + .gdb_command = "gdb", + .debug_signo = SIGHUP, + .async_interval = 0.05, + .async_signo = SIGALRM, + .stats_dest = "", + .tuning_path = "", + .instrument_file = "", + .instrument_max_size = 1048576, + .memtrack_dest = "", +#if ENABLE_STATS + .stats_dest = "", + .stats_trigger = "exit", +#endif +#if ENABLE_MEMTRACK + .memtrack_dest = "", +#endif +#if HAVE_INSTRUMENTATION + .instrument_file = "", + .instrument_max_size = 1048576, +#endif +}; + +static const char *handle_error_modes[] = { + [UCS_HANDLE_ERROR_NONE] = "none", + [UCS_HANDLE_ERROR_BACKTRACE] = "bt", + [UCS_HANDLE_ERROR_FREEZE] = "freeze", + [UCS_HANDLE_ERROR_DEBUG] = "debug", + [UCS_HANDLE_ERROR_LAST] = NULL +}; + +static UCS_CONFIG_DEFINE_ARRAY(signo, + sizeof(int), + UCS_CONFIG_TYPE_SIGNO); + +ucs_config_field_t ucs_global_opts_table[] = { + {"LOG_LEVEL", "warn", + "UCS logging level. Messages with a level higher or equal to the selected " + "will be printed.\n" + "Possible values are: fatal, error, warn, info, debug, trace, data, func, poll.", + ucs_offsetof(ucs_global_opts_t, log_level), UCS_CONFIG_TYPE_ENUM(ucs_log_level_names)}, + + {"LOG_FILE", "", + "If not empty, UCS will print log messages to the specified file instead of stdout.\n" + "The following substitutions are performed on this string:\n" + " %p - Replaced with process ID\n" + " %h - Replaced with host name\n", + ucs_offsetof(ucs_global_opts_t, log_file), + UCS_CONFIG_TYPE_STRING}, + + {"LOG_BUFFER", "1024", + "Buffer size for a single log message.", + ucs_offsetof(ucs_global_opts_t, log_buffer_size), UCS_CONFIG_TYPE_MEMUNITS}, + + {"LOG_DATA_SIZE", "0", + "How much packet payload to print, at most, in data mode.", + ucs_offsetof(ucs_global_opts_t, log_data_size), UCS_CONFIG_TYPE_ULONG}, + + {"HANDLE_ERRORS", "bt", + "Error handling mode. Possible values are: 'none' (no error handling), 'bt' (print\n" + "backtrace), 'freeze' (freeze and wait for a debugger), 'debug' (attach debugger)", + ucs_offsetof(ucs_global_opts_t, handle_errors), UCS_CONFIG_TYPE_ENUM(handle_error_modes)}, + + {"ERROR_SIGNALS", "SIGILL,SIGSEGV,SIGBUS,SIGFPE", + "Signals which are considered an error indication and trigger error handling.", + ucs_offsetof(ucs_global_opts_t, error_signals), UCS_CONFIG_TYPE_ARRAY(signo)}, + + {"GDB_COMMAND", "gdb", + "If non-empty, attaches a gdb to the process in case of error, using the provided command.", + ucs_offsetof(ucs_global_opts_t, gdb_command), UCS_CONFIG_TYPE_STRING}, + + {"DEBUG_SIGNO", "SIGHUP", + "Signal number which causes UCS to enter debug mode. Set to 0 to disable.", + ucs_offsetof(ucs_global_opts_t, debug_signo), UCS_CONFIG_TYPE_SIGNO}, + + {"ASYNC_INTERVAL", "50ms", + "Interval of asynchronous progress. Lower values may make the network\n" + "more responsive, in the cost of higher CPU load.", + ucs_offsetof(ucs_global_opts_t, async_interval), UCS_CONFIG_TYPE_TIME}, + + {"ASYNC_SIGNO", "SIGALRM", + "Signal number used for async signaling.", + ucs_offsetof(ucs_global_opts_t, async_signo), UCS_CONFIG_TYPE_SIGNO}, + +#if ENABLE_STATS + {"STATS_DEST", "", + "Destination to send statistics to. If the value is empty, statistics are\n" + "not reported. Possible values are:\n" + " udp:[:] - send over UDP to the given host:port.\n" + " stdout - print to standard output.\n" + " stderr - print to standard error.\n" + " file:[:bin] - save to a file (%h: host, %p: pid, %c: cpu, %t: time, %u: user, %e: exe)", + ucs_offsetof(ucs_global_opts_t, stats_dest), UCS_CONFIG_TYPE_STRING}, + + {"STATS_TRIGGER", "exit", + "Trigger to dump statistics:\n" + " exit - dump just before program exits.\n" + " signal: - dump when process is signaled.\n" + " timer: - dump in specified intervals (in seconds).", + ucs_offsetof(ucs_global_opts_t, stats_trigger), UCS_CONFIG_TYPE_STRING}, + +#endif + +#if ENABLE_MEMTRACK + {"MEMTRACK_DEST", "", + "Destination to output memory tracking report to. If the value is empty,\n" + "results are not reported. Possible values are:\n" + " file: - save to a file (%h: host, %p: pid, %c: cpu, %t: time, %u: user, %e: exe)\n" + " stdout - print to standard output.\n" + " stderr - print to standard error.\n", + ucs_offsetof(ucs_global_opts_t, memtrack_dest), UCS_CONFIG_TYPE_STRING}, +#endif + +#if HAVE_INSTRUMENTATION + {"INSTRUMENT", "", + "File name to dump instrumentation records to.\n" + "Substitutions: %h: host, %p: pid, %c: cpu, %t: time, %u: user, %e: exe.\n", + ucs_offsetof(ucs_global_opts_t, instrument_file), + UCS_CONFIG_TYPE_STRING}, + + {"INSTRUMENT_SIZE", "1048576", + "Maximal size of instrumentation data. New records will replace old records.", + ucs_offsetof(ucs_global_opts_t, instrument_max_size), + UCS_CONFIG_TYPE_MEMUNITS}, +#endif + + {NULL} +}; + +void UCS_F_CTOR ucs_global_opts_init() +{ + ucs_config_parser_fill_opts(&ucs_global_opts, ucs_global_opts_table, NULL); +} diff --git a/src/ucs/config/global_opts.h b/src/ucs/config/global_opts.h new file mode 100644 index 00000000000..2258abb99c2 --- /dev/null +++ b/src/ucs/config/global_opts.h @@ -0,0 +1,87 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_CONFIG_H_ +#define UCS_CONFIG_H_ + +#include "types.h" +#include "parser.h" + +#include + + +/** + * UCS global options. + */ +typedef struct { + + /* Log level above which log messages will be printed */ + ucs_log_level_t log_level; + + /* Log file */ + char *log_file; + + /* Size of log buffer for one message */ + size_t log_buffer_size; + + /* Maximal amount of packet data to print per packet */ + size_t log_data_size; + + /* Handle errors mode */ + ucs_handle_error_t handle_errors; + + /* Error signals */ + struct { + int *signals; + unsigned count; + } error_signals; + + /* If not NULL, attach gdb to the process in case of error */ + char *gdb_command; + + /* Signal number which causes to enter debug mode */ + unsigned debug_signo; + + /* File name to dump instrumentation records to */ + char *instrument_file; + + /* Limit for instrumentation data size */ + size_t instrument_max_size; + + /* Asynchronous progress interval. Lower values may cause poor performance, + * but better responsiveness. + */ + double async_interval; + + /* Destination for statistics: udp:host:port / file:path / stdout + */ + char *stats_dest; + + /* Trigger to dump statistics */ + char *stats_trigger; + + /* Named pipe file path for tuning. + */ + char *tuning_path; + + /* Number of performance stall loops to perform */ + size_t perf_stall_loops; + + /* Signal number used by async handler (for signal mode) */ + unsigned async_signo; + + /* Destination for detailed memory tracking results: none / stdout / stderr + */ + char *memtrack_dest; + +} ucs_global_opts_t; + + +extern ucs_global_opts_t ucs_global_opts; +extern ucs_config_field_t ucs_global_opts_table[]; + +#endif diff --git a/src/ucs/config/parser.c b/src/ucs/config/parser.c new file mode 100644 index 00000000000..db3ed3a218e --- /dev/null +++ b/src/ucs/config/parser.c @@ -0,0 +1,1133 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#define _GNU_SOURCE +#include "parser.h" + +#include +#include +#include +#include + + +#define UCS_CONFIG_ARRAY_MAX 128 + +typedef struct ucs_config_array_field { + void *data; + unsigned count; +} ucs_config_array_field_t; + + +/* Process environment variables */ +extern char **environ; + +/* Fwd */ +static ucs_status_t +ucs_config_parser_set_value_internal(void *opts, ucs_config_field_t *fields, + const char *name, const char *value, + const char *table_prefix, int recurse); + + +static int __find_string_in_list(const char *str, const char **list) +{ + int i; + + for (i = 0; *list; ++list, ++i) { + if (strcasecmp(*list, str) == 0) { + return i; + } + } + return -1; +} + +int ucs_config_sscanf_string(const char *buf, void *dest, const void *arg) +{ + *((char**)dest) = strdup(buf); + return 1; +} + +int ucs_config_sprintf_string(char *buf, size_t max, void *src, const void *arg) +{ + strncpy(buf, *((char**)src), max); + return 1; +} + +ucs_status_t ucs_config_clone_string(void *src, void *dest, const void *arg) +{ + char *new_str = strdup(*(char**)src); + if (new_str == NULL) { + return UCS_ERR_NO_MEMORY; + } + + *((char**)dest) = new_str; + return UCS_OK; +} + +void ucs_config_release_string(void *ptr, const void *arg) +{ + free(*(char**)ptr); +} + +int ucs_config_sscanf_int(const char *buf, void *dest, const void *arg) +{ + return sscanf(buf, "%i", (unsigned*)dest); +} + +ucs_status_t ucs_config_clone_int(void *src, void *dest, const void *arg) +{ + *(int*)dest = *(int*)src; + return UCS_OK; +} + +int ucs_config_sprintf_int(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%i", *(unsigned*)src); +} + +int ucs_config_sscanf_uint(const char *buf, void *dest, const void *arg) +{ + return sscanf(buf, "%u", (unsigned*)dest); +} + +ucs_status_t ucs_config_clone_uint(void *src, void *dest, const void *arg) +{ + *(unsigned*)dest = *(unsigned*)src; + return UCS_OK; +} + +int ucs_config_sprintf_uint(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%u", *(unsigned*)src); +} + +int ucs_config_sscanf_ulong(const char *buf, void *dest, const void *arg) +{ + return sscanf(buf, "%lu", (unsigned long*)dest); +} + +int ucs_config_sprintf_ulong(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%lu", *(unsigned long*)src); +} + +ucs_status_t ucs_config_clone_ulong(void *src, void *dest, const void *arg) +{ + *(unsigned long*)dest = *(unsigned long*)src; + return UCS_OK; +} + +int ucs_config_sscanf_double(const char *buf, void *dest, const void *arg) +{ + return sscanf(buf, "%lf", (double*)dest); +} + +int ucs_config_sprintf_double(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%.3f", *(double*)src); +} + +ucs_status_t ucs_config_clone_double(void *src, void *dest, const void *arg) +{ + *(double*)dest = *(double*)src; + return UCS_OK; +} + +int ucs_config_sscanf_bool(const char *buf, void *dest, const void *arg) +{ + if (!strcasecmp(buf, "y") || !strcasecmp(buf, "yes") || !strcmp(buf, "1")) { + *(int*)dest = 1; + return 1; + } else if (!strcasecmp(buf, "n") || !strcasecmp(buf, "no") || !strcmp(buf, "0")) { + *(int*)dest = 0; + return 1; + } else { + return 0; + } +} + +int ucs_config_sprintf_bool(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%c", *(int*)src ? 'y' : 'n'); +} + +int ucs_config_sscanf_ternary(const char *buf, void *dest, const void *arg) +{ + UCS_STATIC_ASSERT(UCS_NO == 0); + UCS_STATIC_ASSERT(UCS_YES == 1); + if (!strcasecmp(buf, "try") || !strcasecmp(buf, "maybe")) { + *(int*)dest = UCS_TRY; + return 1; + } else { + return ucs_config_sscanf_bool(buf, dest, arg); + } +} + +int ucs_config_sprintf_ternary(char *buf, size_t max, void *src, const void *arg) +{ + if (*(int*)src == UCS_TRY) { + return snprintf(buf, max, "try"); + } else { + return ucs_config_sprintf_bool(buf, max, src, arg); + } +} + +int ucs_config_sscanf_enum(const char *buf, void *dest, const void *arg) +{ + int i; + + i = __find_string_in_list(buf, (const char**)arg); + if (i < 0) { + return 0; + } + + *(unsigned*)dest = i; + return 1; +} + +int ucs_config_sprintf_enum(char *buf, size_t max, void *src, const void *arg) +{ + char * const *table = arg; + strncpy(buf, table[*(unsigned*)src], max); + return 1; +} + +static void __print_table_values(char * const *table, char *buf, size_t max) +{ + char *ptr = buf, *end = buf + max; + + for (; *table; ++table) { + snprintf(ptr, end - ptr, "|%s", *table); + ptr += strlen(ptr); + } + + snprintf(ptr, end - ptr, "]"); + ptr += strlen(ptr); + + *buf = '['; +} + +void ucs_config_help_enum(char *buf, size_t max, const void *arg) +{ + __print_table_values(arg, buf, max); +} + +int ucs_config_sscanf_bitmap(const char *buf, void *dest, const void *arg) +{ + char *str = strdup(buf); + char *p; + int ret, i; + + ret = 1; + *((unsigned*)dest) = 0; + p = strtok(str, ","); + while (p != NULL) { + i = __find_string_in_list(p, (const char**)arg); + if (i < 0) { + ret = 0; + break; + } + *((unsigned*)dest) |= UCS_BIT(i); + p = strtok(NULL, ","); + } + + free(str); + return ret; +} + +int ucs_config_sprintf_bitmap(char *buf, size_t max, void *src, const void *arg) +{ + char * const *table; + int i, len; + + len = 0; + for (table = arg, i = 0; *table; ++table, ++i) { + if (*((unsigned*)src) & UCS_BIT(i)) { + snprintf(buf + len, max - len, "%s,", *table); + len = strlen(buf); + } + } + + if (len > 0) { + buf[len - 1] = '\0'; /* remove last ',' */ + } else { + buf[0] = '\0'; + } + return 1; +} + +void ucs_config_help_bitmap(char *buf, size_t max, const void *arg) +{ + snprintf(buf, max, "comma-separated list of: "); + __print_table_values(arg, buf + strlen(buf), max - strlen(buf)); +} + +int ucs_config_sscanf_bitmask(const char *buf, void *dest, const void *arg) +{ + int ret = sscanf(buf, "%u", (unsigned*)dest); + if (*(unsigned*)dest != 0) { + *(unsigned*)dest = UCS_BIT(*(unsigned*)dest) - 1; + } + return ret; +} + +int ucs_config_sprintf_bitmask(char *buf, size_t max, void *src, const void *arg) +{ + return snprintf(buf, max, "%u", __builtin_popcount(*(unsigned*)src)); +} + +int ucs_config_sscanf_port_spec(const char *buf, void *dest, const void *arg) +{ + ucs_ib_port_spec_t *port_spec = dest; + char *p, *str; + int release; + + str = strdup(buf); + release = 1; + + /* Split */ + p = strchr(str, ':'); + if (p == NULL) { + goto err; + } + *p = 0; + + /* Device name */ + if (!strcmp(str, "*")) { + port_spec->device_name = UCS_IB_CFG_DEVICE_NAME_ALL; + } else if (!strcmp(str, "?")) { + port_spec->device_name = UCS_IB_CFG_DEVICE_NAME_ANY; + } else { + port_spec->device_name = str; + release = 0; + } + + /* Port number */ + if (!strcmp(p + 1, "*")) { + port_spec->port_num = UCS_IB_CFG_PORT_NUM_ALL; + } else if (!strcmp(p + 1, "?")) { + port_spec->port_num = UCS_IB_CFG_PORT_NUM_ANY; + } else if (1 == sscanf(p + 1, "%d", &port_spec->port_num)) { + /* OK */ + } else { + goto err; + } + + if (release) { + free(str); + } + return 1; + +err: + free(str); + return 0; +} + +int ucs_config_sprintf_port_spec(char *buf, size_t max, void *src, const void *arg) +{ + ucs_ib_port_spec_t *port_spec = src; + const char *device_name_str; + + if (port_spec->device_name == UCS_IB_CFG_DEVICE_NAME_ALL) { + device_name_str = "*"; + } else if (port_spec->device_name == UCS_IB_CFG_DEVICE_NAME_ANY) { + device_name_str = "?"; + } else { + device_name_str = port_spec->device_name; + } + + if (port_spec->port_num == UCS_IB_CFG_PORT_NUM_ALL) { + snprintf(buf, max, "%s:*", device_name_str); + } else if (port_spec->port_num == UCS_IB_CFG_PORT_NUM_ANY) { + snprintf(buf, max, "%s:?", device_name_str); + } else { + snprintf(buf, max, "%s:%d", device_name_str, port_spec->port_num); + } + + return 1; +} + +ucs_status_t ucs_config_clone_port_spec(void *src, void *dest, const void *arg) +{ + ucs_ib_port_spec_t *src_port_spec = src, *dest_port_spec = dest; + + if (src_port_spec->device_name == UCS_IB_CFG_DEVICE_NAME_ALL || + src_port_spec->device_name == UCS_IB_CFG_DEVICE_NAME_ANY) { + dest_port_spec->device_name = src_port_spec->device_name; + } else { + dest_port_spec->device_name = strdup(src_port_spec->device_name); + if (dest_port_spec->device_name == NULL) { + return UCS_ERR_NO_MEMORY; + } + } + + dest_port_spec->port_num = src_port_spec->port_num; + return UCS_OK; +} + +void ucs_config_release_port_spec(void *ptr, const void *arg) +{ + ucs_ib_port_spec_t *port_spec = ptr; + if (port_spec->device_name != UCS_IB_CFG_DEVICE_NAME_ALL && + port_spec->device_name != UCS_IB_CFG_DEVICE_NAME_ANY) { + free(port_spec->device_name); + } +} + +int ucs_config_sscanf_time(const char *buf, void *dest, const void *arg) +{ + char units[3]; + int num_fields; + double value; + double per_sec; + + memset(units, 0, sizeof(units)); + num_fields = sscanf(buf, "%lf%c%c", &value, &units[0], &units[1]); + if (num_fields == 1) { + per_sec = 1; + } else if (num_fields == 2 || num_fields == 3) { + if (!strcmp(units, "m")) { + per_sec = 1.0 / 60.0; + } else if (!strcmp(units, "s")) { + per_sec = 1; + } else if (!strcmp(units, "ms")) { + per_sec = UCS_MSEC_PER_SEC; + } else if (!strcmp(units, "us")) { + per_sec = UCS_USEC_PER_SEC; + } else if (!strcmp(units, "ns")) { + per_sec = UCS_NSEC_PER_SEC; + } else { + return 0; + } + } else { + return 0; + } + + *(double*)dest = value / per_sec; + return 1; +} + +int ucs_config_sprintf_time(char *buf, size_t max, void *src, const void *arg) +{ + snprintf(buf, max, "%.2fus", *(double*)src * UCS_USEC_PER_SEC); + return 1; +} + +int ucs_config_sscanf_signo(const char *buf, void *dest, const void *arg) +{ + char *endptr; + int signo; + + signo = strtol(buf, &endptr, 10); + if (*endptr == '\0') { + *(int*)dest = signo; + return 1; + } + + if (!strncmp(buf, "SIG", 3)) { + buf += 3; + } + + return ucs_config_sscanf_enum(buf, dest, ucs_signal_names); +} + +int ucs_config_sprintf_signo(char *buf, size_t max, void *src, const void *arg) +{ + return ucs_config_sprintf_enum(buf, max, src, ucs_signal_names); +} + +int ucs_config_sscanf_memunits(const char *buf, void *dest, const void *arg) +{ + char units[3]; + int num_fields; + size_t value; + size_t bytes; + + /* Special value: infinity */ + if (!strcasecmp(buf, "inf")) { + *(size_t*)dest = ULONG_MAX; + return 1; + } + + memset(units, 0, sizeof(units)); + num_fields = sscanf(buf, "%ld%c%c", &value, &units[0], &units[1]); + if (num_fields == 1) { + bytes = 1; + } else if (num_fields == 2 || num_fields == 3) { + if (!strcasecmp(units, "b")) { + bytes = 1; + } else if (!strcasecmp(units, "kb") || !strcasecmp(units, "k")) { + bytes = UCS_KBYTE; + } else if (!strcasecmp(units, "mb") || !strcasecmp(units, "m")) { + bytes = UCS_MBYTE; + } else if (!strcasecmp(units, "gb") || !strcasecmp(units, "g")) { + bytes = UCS_GBYTE; + } else { + return 0; + } + } else { + return 0; + } + + *(size_t*)dest = value * bytes; + return 1; +} + +int ucs_config_sprintf_memunits(char *buf, size_t max, void *src, const void *arg) +{ + size_t sz = *(size_t*)src; + + if (sz == ULONG_MAX) { + snprintf(buf, max, "inf"); + } else { + snprintf(buf, max, "%Zu", sz); + } + return 1; +} + +int ucs_config_sscanf_array(const char *buf, void *dest, const void *arg) +{ + ucs_config_array_field_t *field = dest; + const ucs_config_array_t *array = arg; + char *dup, *token, *saveptr; + int ret; + unsigned i; + + dup = strdup(buf); + saveptr = NULL; + token = strtok_r(dup, ",", &saveptr); + field->data = ucs_calloc(UCS_CONFIG_ARRAY_MAX, array->elem_size, "config array"); + i = 0; + while (token != NULL) { + ret = array->parser.read(token, (char*)field->data + i * array->elem_size, + array->parser.arg); + if (!ret) { + ucs_free(field->data); + free(dup); + return 0; + } + + ++i; + if (i >= UCS_CONFIG_ARRAY_MAX) { + break; + } + token = strtok_r(NULL, ",", &saveptr); + } + + field->count = i; + free(dup); + return 1; +} + +int ucs_config_sprintf_array(char *buf, size_t max, void *src, const void *arg) +{ + ucs_config_array_field_t *field = src; + const ucs_config_array_t *array = arg; + size_t offset; + unsigned i; + int ret; + + offset = 0; + for (i = 0; i < field->count; ++i) { + if (i > 0 && offset < max) { + buf[offset++] = ','; + } + ret = array->parser.write(buf + offset, max - offset, + (char*)field->data + i * array->elem_size, + array->parser.arg); + if (!ret) { + return 0; + } + + offset += strlen(buf + offset); + } + return 1; +} + +ucs_status_t ucs_config_clone_array(void *src, void *dest, const void *arg) +{ + ucs_config_array_field_t *dest_array = dest, *src_array = src; + const ucs_config_array_t *array = arg; + ucs_status_t status; + unsigned i; + + dest_array->data = ucs_calloc(src_array->count, array->elem_size, + "config array"); + if (dest_array->data == NULL) { + return UCS_ERR_NO_MEMORY; + } + + dest_array->count = src_array->count; + for (i = 0; i < src_array->count; ++i) { + status = array->parser.clone((char*)src_array->data + i * array->elem_size, + (char*)dest_array->data + i * array->elem_size, + array->parser.arg); + if (status != UCS_OK) { + ucs_free(dest_array->data); + return status; + } + } + + return UCS_OK; +} + +void ucs_config_release_array(void *ptr, const void *arg) +{ + ucs_config_array_field_t *array_field = ptr; + const ucs_config_array_t *array = arg; + unsigned i; + + for (i = 0; i < array_field->count; ++i) { + array->parser.release((char*)array_field->data + i * array->elem_size, + array->parser.arg); + } + ucs_free(array_field->data); +} + +void ucs_config_help_array(char *buf, size_t max, const void *arg) +{ + const ucs_config_array_t *array = arg; + + snprintf(buf, max, "comma-separated list of: "); + array->parser.help(buf + strlen(buf), max - strlen(buf), array->parser.arg); +} + +int ucs_config_sscanf_table(const char *buf, void *dest, const void *arg) +{ + char *tokens = strdupa(buf); + char *token, *saveptr1; + char *name, *value, *saveptr2; + ucs_status_t status; + + saveptr1 = NULL; + saveptr2 = NULL; + token = strtok_r(tokens, ";", &saveptr1); + while (token != NULL) { + + name = strtok_r(token, "=", &saveptr2); + value = strtok_r(NULL, "=", &saveptr2); + if (value == NULL) { + ucs_error("Could not parse list of values in '%s' (token: '%s')", buf, token); + return 0; + } + + status = ucs_config_parser_set_value_internal(dest, (ucs_config_field_t*)arg, + name, value, NULL, 1); + if (status != UCS_OK) { + if (status == UCS_ERR_NO_ELEM) { + ucs_error("Field '%s' does not exist", name); + } else { + ucs_debug("Failed to set %s to '%s': %s", name, value, + ucs_status_string(status)); + } + return 0; + } + + token = strtok_r(NULL, ";", &saveptr1); + } + + return 1; +} + +ucs_status_t ucs_config_clone_table(void *src, void *dst, const void *arg) +{ + return ucs_config_parser_clone_opts(src, dst, (ucs_config_field_t*)arg); +} + +void ucs_config_release_table(void *ptr, const void *arg) +{ + ucs_config_parser_release_opts(ptr, (ucs_config_field_t*)arg); +} + +void ucs_config_help_table(char *buf, size_t max, const void *arg) +{ + snprintf(buf, max, "Table"); +} + +void ucs_config_release_nop(void *ptr, const void *arg) +{ +} + +void ucs_config_help_generic(char *buf, size_t max, const void *arg) +{ + strncpy(buf, (char*)arg, max); +} + +static inline int ucs_config_is_alias_field(const ucs_config_field_t *field) +{ + return (field->dfl_value == NULL); +} + +static inline int ucs_config_is_table_field(const ucs_config_field_t *field) +{ + return (field->parser.read == ucs_config_sscanf_table); +} + +static void ucs_config_print_doc_line_by_line(const ucs_config_field_t *field, + void (*cb)(int num, const char *line, void *arg), + void *arg) +{ + char *doc, *line, *p; + int num; + + line = doc = strdup(field->doc); + p = strchr(line, '\n'); + num = 0; + while (p != NULL) { + *p = '\0'; + cb(num, line, arg); + line = p + 1; + p = strchr(line, '\n'); + ++num; + } + cb(num, line, arg); + free(doc); +} + +static ucs_status_t +ucs_config_parser_parse_field(ucs_config_field_t *field, const char *value, void *var) +{ + char syntax_buf[256]; + int ret; + + ret = field->parser.read(value, var, field->parser.arg); + if (ret != 1) { + if (ucs_config_is_table_field(field)) { + ucs_error("Could not set table value for %s: '%s'", field->name, value); + + } else { + field->parser.help(syntax_buf, sizeof(syntax_buf) - 1, field->parser.arg); + ucs_error("Invalid value for %s: '%s'. Expected: %s", field->name, + value, syntax_buf); + } + return UCS_ERR_INVALID_PARAM; + } + + return UCS_OK; +} + +static void ucs_config_parser_release_field(ucs_config_field_t *field, void *var) +{ + field->parser.release(var, field->parser.arg); +} + +static ucs_status_t +ucs_config_parser_set_default_values(void *opts, ucs_config_field_t *fields) +{ + ucs_config_field_t *field, *sub_fields; + ucs_status_t status; + void *var; + + for (field = fields; field->name; ++field) { + if (ucs_config_is_alias_field(field)) { + continue; + } + + var = (char*)opts + field->offset; + + /* If this field is a sub-table, recursively set the values for it. + * Defaults can be subsequently set by parser.read(). */ + if (ucs_config_is_table_field(field)) { + sub_fields = (ucs_config_field_t*)field->parser.arg; + status = ucs_config_parser_set_default_values(var, sub_fields); + if (status != UCS_OK) { + return status; + } + } + + status = ucs_config_parser_parse_field(field, field->dfl_value, var); + if (status != UCS_OK) { + return status; + } + } + + return UCS_OK; +} + +/** + * table_prefix == NULL -> unused + */ +static ucs_status_t +ucs_config_parser_set_value_internal(void *opts, ucs_config_field_t *fields, + const char *name, const char *value, + const char *table_prefix, int recurse) +{ + ucs_config_field_t *field, *sub_fields; + size_t prefix_len; + ucs_status_t status; + unsigned count; + void *var; + + prefix_len = (table_prefix == NULL) ? 0 : strlen(table_prefix); + + count = 0; + for (field = fields; field->name; ++field) { + + var = (char*)opts + field->offset; + + if (ucs_config_is_table_field(field)) { + sub_fields = (ucs_config_field_t*)field->parser.arg; + + /* Check with sub-table prefix */ + if (recurse) { + status = ucs_config_parser_set_value_internal(var, sub_fields, + name, value, + field->name, 1); + if (status == UCS_OK) { + ++count; + } else if (status != UCS_ERR_NO_ELEM) { + return status; + } + } + + /* Possible override with my prefix */ + if (table_prefix != NULL) { + status = ucs_config_parser_set_value_internal(var, sub_fields, + name, value, + table_prefix, 0); + if (status == UCS_OK) { + ++count; + } else if (status != UCS_ERR_NO_ELEM) { + return status; + } + } + } else if (((table_prefix == NULL) || !strncmp(name, table_prefix, prefix_len)) && + !strcmp(name + prefix_len, field->name)) + { + ucs_config_parser_release_field(field, var); + status = ucs_config_parser_parse_field(field, value, var); + if (status != UCS_OK) { + return status; + } + ++count; + } + } + + return (count == 0) ? UCS_ERR_NO_ELEM : UCS_OK; +} + +static ucs_status_t ucs_config_apply_env_vars(void *opts, ucs_config_field_t *fields, + const char *prefix, const char *table_prefix, + int recurse) +{ + ucs_config_field_t *field, *sub_fields; + ucs_status_t status; + size_t prefix_len; + const char *env_value; + void *var; + char buf[256]; + + /* Put prefix in the buffer. Later we replace only the variable name part */ + snprintf(buf, sizeof(buf) - 1, "%s%s", prefix, table_prefix ? table_prefix : ""); + prefix_len = strlen(buf); + + /* Parse environment variables */ + for (field = fields; field->name; ++field) { + + var = (char*)opts + field->offset; + + if (ucs_config_is_table_field(field)) { + sub_fields = (ucs_config_field_t*)field->parser.arg; + + /* Parse with sub-table prefix */ + if (recurse) { + status = ucs_config_apply_env_vars(var, sub_fields, prefix, field->name, 1); + if (status != UCS_OK) { + return status; + } + } + + /* Possible override with my prefix */ + if (table_prefix) { + status = ucs_config_apply_env_vars(var, sub_fields, prefix, table_prefix, 0); + if (status != UCS_OK) { + return status; + } + } + } else { + /* Read and parse environment variable */ + strncpy(buf + prefix_len, field->name, sizeof(buf) - prefix_len - 1); + env_value = getenv(buf); + if (env_value != NULL) { + ucs_config_parser_release_field(field, var); + status = ucs_config_parser_parse_field(field, env_value, var); + if (status != UCS_OK) { + return status; + } + } + } + } + + return UCS_OK; +} + +ucs_status_t ucs_config_parser_fill_opts(void *opts, ucs_config_field_t *table, + const char *user_prefix) +{ + ucs_status_t status; + char *full_prefix; + int ret; + + status = ucs_config_parser_set_default_values(opts, table); + if (status != UCS_OK) { + return status; + } + + /* Use default UCS_ prefix */ + status = ucs_config_apply_env_vars(opts, table, UCS_CONFIG_VAR_PREFIX, NULL, 1); + if (status != UCS_OK) { + return status; + } + + /* Override values with user-defined prefix */ + if (user_prefix != NULL) { + ret = asprintf(&full_prefix, "%s%s_", UCS_CONFIG_VAR_PREFIX, user_prefix); + if (ret < 0) { + return UCS_ERR_NO_MEMORY; + } + + status = ucs_config_apply_env_vars(opts, table, full_prefix, NULL, 1); + free(full_prefix); + if (status != UCS_OK) { + return status; + } + } + + // TODO cleanup existing allocated values on error flow + + return UCS_OK; +} + + +ucs_status_t ucs_config_parser_read_opts(ucs_config_field_t *table, + const char *user_prefix, + size_t size, void **optsp) +{ + ucs_status_t status; + void* opts; + + opts = ucs_calloc(1, size, "opts"); + if (opts == NULL) { + status = UCS_ERR_NO_MEMORY; + goto err; + } + + status = ucs_config_parser_fill_opts(opts, table, user_prefix); + if (status != UCS_OK) { + goto err_free_opts; + } + + *optsp = opts; + return UCS_OK; + +err_free_opts: + ucs_free(opts); +err: + return status; +} + + +ucs_status_t ucs_config_parser_set_value(void *opts, ucs_config_field_t *fields, + const char *name, const char *value) +{ + return ucs_config_parser_set_value_internal(opts, fields, name, value, NULL, 1); +} + +ucs_status_t ucs_config_parser_get_value(void *opts, ucs_config_field_t *fields, + const char *name, char *value, size_t max) +{ + ucs_config_field_t *field; + void *value_ptr; + + for (field = fields; field->name; ++field) { + //TODO table + if (!strcmp(field->name, name)) { + value_ptr = (char*)opts + field->offset; + field->parser.write(value, max, value_ptr, field->parser.arg); + return UCS_OK; + } + } + + return UCS_ERR_INVALID_PARAM; +} + +ucs_status_t ucs_config_parser_clone_opts(void *src, void *dst, + ucs_config_field_t *fields) +{ + ucs_status_t status; + + ucs_config_field_t *field; + for (field = fields; field->name; ++field) { + if (ucs_config_is_alias_field(field)) { + continue; + } + + status = field->parser.clone((char*)src + field->offset, + (char*)dst + field->offset, + field->parser.arg); + if (status != UCS_OK) { + ucs_error("Failed to clone the filed '%s': %s", field->name, + ucs_status_string(status)); + return status; + } + } + + return UCS_OK; +} + +void ucs_config_parser_release_opts(void *opts, ucs_config_field_t *fields) +{ + ucs_config_field_t *field; + + for (field = fields; field->name; ++field) { + if (ucs_config_is_alias_field(field)) { + continue; + } + + ucs_config_parser_release_field(field, (char*)opts + field->offset); + } +} + +/* + * Finds the "real" field, which the given field is alias of. + * *p_alias_table_offset is filled with the offset of the sub-table containing + * the field, it may be non-0 if the alias is found in a sub-table. + */ +static const ucs_config_field_t * +ucs_config_find_aliased_field(const ucs_config_field_t *fields, + const ucs_config_field_t *alias, + size_t *p_alias_table_offset) +{ + const ucs_config_field_t *field, *result; + size_t offset; + + for (field = fields; field->name; ++field) { + + if (field == alias) { + /* skip */ + } else if (ucs_config_is_table_field(field)) { + result = ucs_config_find_aliased_field(field->parser.arg, alias, + &offset); + if (result != NULL) { + *p_alias_table_offset = offset + field->offset; + return result; + } + } else if (field->offset == alias->offset) { + *p_alias_table_offset = 0; + return field; + } + } + + return NULL; +} + +static void __print_stream_cb(int num, const char *line, void *arg) +{ + FILE *stream = arg; + fprintf(stream, "# %s\n", line); +} + +static void +ucs_config_parser_print_field(FILE *stream, void *opts, const char *prefix, + const char *name, const ucs_config_field_t *field, + unsigned long flags, const char *docstr, ...) +{ + char value_buf[128] = {0}; + char syntax_buf[256] = {0}; + va_list ap; + + field->parser.write(value_buf, sizeof(value_buf) - 1, (char*)opts + field->offset, + field->parser.arg); + field->parser.help(syntax_buf, sizeof(syntax_buf) - 1, field->parser.arg); + + if (flags & UCS_CONFIG_PRINT_DOC) { + fprintf(stream, "#\n"); + ucs_config_print_doc_line_by_line(field, __print_stream_cb, stream); + fprintf(stream, "#\n"); + fprintf(stream, "# Value: %s\n", syntax_buf); + fprintf(stream, "#\n"); + + /* Extra docstring */ + if (docstr != NULL) { + fprintf(stream, "# "); + va_start(ap, docstr); + vfprintf(stream, docstr, ap); + va_end(ap); + fprintf(stream, "\n"); + } + } + + fprintf(stream, "%s%s%s=%s\n", UCS_CONFIG_VAR_PREFIX, prefix, name, value_buf); + + if (flags & UCS_CONFIG_PRINT_DOC) { + fprintf(stream, "\n"); + } +} + +static void +ucs_config_parser_print_opts_recurs(FILE *stream, void *opts, + const ucs_config_field_t *fields, + unsigned flags, const char *table_prefix) +{ + const ucs_config_field_t *field, *aliased_field; + size_t alias_table_offset; + const char *prefix; + + prefix = table_prefix == NULL ? "" : table_prefix; + + for (field = fields; field->name; ++field) { + if (ucs_config_is_table_field(field)) { + /* Parse with sub-table prefix */ + if (table_prefix == NULL) { + ucs_config_parser_print_opts_recurs(stream, opts + field->offset, + field->parser.arg, flags, + field->name); + } else { + ucs_config_parser_print_opts_recurs(stream, opts + field->offset, + field->parser.arg, flags, + table_prefix); + } + } else if (ucs_config_is_alias_field(field)) { + if (flags & UCS_CONFIG_PRINT_HIDDEN) { + aliased_field = ucs_config_find_aliased_field(fields, field, + &alias_table_offset); + if (aliased_field == NULL) { + ucs_fatal("could not find aliased field of %s", field->name); + } + ucs_config_parser_print_field(stream, + opts + alias_table_offset, + table_prefix, + field->name, aliased_field, + flags, "(alias of %s%s%s)", + UCS_CONFIG_VAR_PREFIX, table_prefix, + aliased_field->name); + } + } else { + ucs_config_parser_print_field(stream, opts, prefix, field->name, + field, flags, NULL); + } + } + +} + +void ucs_config_parser_print_opts(FILE *stream, const char *title, void *opts, + ucs_config_field_t *fields, unsigned flags) +{ + if (flags & UCS_CONFIG_PRINT_HEADER) { + fprintf(stream, "\n"); + fprintf(stream, "#\n"); + fprintf(stream, "# %s\n", title); + fprintf(stream, "#\n"); + fprintf(stream, "\n"); + } + + ucs_config_parser_print_opts_recurs(stream, opts, fields, flags, NULL); + + if (flags & UCS_CONFIG_PRINT_HEADER) { + fprintf(stream, "\n"); + } +} + diff --git a/src/ucs/config/parser.h b/src/ucs/config/parser.h new file mode 100644 index 00000000000..388457d1f91 --- /dev/null +++ b/src/ucs/config/parser.h @@ -0,0 +1,279 @@ +/* +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_CONFIG_PARSER_H +#define UCS_CONFIG_PARSER_H + +#include "types.h" + +#include +#include +#include + + +/* + * Configuration varaibles syntax: + * + * name: + * + * - ucs_prefix: UCS_ + * - user_prefix: supplied by user to ucs_config_read_XXX() API + * - table_prefix: defined in sub-tables. e.g IB_, UD_, ... + * - field_name: field_name as defined in the table. e.g ZCOPY_THRESH + * + * Examples of full variable names: + * - UCS_CIB_RNDV_THRESH + * - UCS_IB_TX_MODERATION + */ + +#define UCS_CONFIG_VAR_PREFIX "UCS_" + +/* Special values for IB port parser */ +#define UCS_IB_CFG_DEVICE_NAME_ANY ((char *)0xfe) +#define UCS_IB_CFG_DEVICE_NAME_ALL ((char *)0xff) +#define UCS_IB_CFG_PORT_NUM_ANY 0xfffe +#define UCS_IB_CFG_PORT_NUM_ALL 0xffff + + +/** + * Configuration printing flags + */ +enum { + UCS_CONFIG_PRINT_HEADER = UCS_BIT(0), + UCS_CONFIG_PRINT_VERSION = UCS_BIT(1), + UCS_CONFIG_PRINT_DOC = UCS_BIT(2), + UCS_CONFIG_PRINT_GLOBAL_OPTS = UCS_BIT(3), + UCS_CONFIG_PRINT_CONTEXT_OPTS = UCS_BIT(4), + UCS_CONFIG_PRINT_ENDPOINT_OPTS = UCS_BIT(5), + UCS_CONFIG_PRINT_HIDDEN = UCS_BIT(6), + UCS_CONFIG_PRINT_BUILD_CONFIG = UCS_BIT(7), + + UCS_CONFIG_PRINT_FULL = UCS_CONFIG_PRINT_HEADER | + UCS_CONFIG_PRINT_VERSION | + UCS_CONFIG_PRINT_DOC | + UCS_CONFIG_PRINT_GLOBAL_OPTS | + UCS_CONFIG_PRINT_CONTEXT_OPTS | + UCS_CONFIG_PRINT_ENDPOINT_OPTS | + UCS_CONFIG_PRINT_HIDDEN | + UCS_CONFIG_PRINT_BUILD_CONFIG +}; + + +typedef struct ucs_config_parser { + int (*read) (const char *buf, void *dest, const void *arg); + int (*write)(char *buf, size_t max, void *src, const void *arg); + ucs_status_t (*clone)(void *src, void *dest, const void *arg); + void (*release)(void *ptr, const void *arg); + void (*help)(char *buf, size_t max, const void *arg); + const void *arg; +} ucs_config_parser_t; + + +typedef struct ucs_config_array { + size_t elem_size; + ucs_config_parser_t parser; +} ucs_config_array_t; + + +typedef struct ucs_config_field { + const char *name; + const char *dfl_value; + const char *doc; + size_t offset; + ucs_config_parser_t parser; +} ucs_config_field_t; + + +typedef struct ucs_ib_port_spec { + char *device_name; + unsigned port_num; +} ucs_ib_port_spec_t; + + +/* + * Parsing and printing different data types + */ + +int ucs_config_sscanf_string(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_string(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_string(void *src, void *dest, const void *arg); +void ucs_config_release_string(void *ptr, const void *arg); + +int ucs_config_sscanf_int(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_int(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_int(void *src, void *dest, const void *arg); + +int ucs_config_sscanf_uint(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_uint(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_uint(void *src, void *dest, const void *arg); + +int ucs_config_sscanf_ulong(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_ulong(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_ulong(void *src, void *dest, const void *arg); + +int ucs_config_sscanf_double(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_double(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_double(void *src, void *dest, const void *arg); + +int ucs_config_sscanf_bool(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_bool(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_ternary(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_ternary(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_enum(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_enum(char *buf, size_t max, void *src, const void *arg); +void ucs_config_help_enum(char *buf, size_t max, const void *arg); + +int ucs_config_sscanf_bitmap(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_bitmap(char *buf, size_t max, void *src, const void *arg); +void ucs_config_help_bitmap(char *buf, size_t max, const void *arg); + +int ucs_config_sscanf_bitmask(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_bitmask(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_port_spec(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_port_spec(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_port_spec(void *src, void *dest, const void *arg); +void ucs_config_release_port_spec(void *ptr, const void *arg); + +int ucs_config_sscanf_time(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_time(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_signo(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_signo(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_memunits(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_memunits(char *buf, size_t max, void *src, const void *arg); + +int ucs_config_sscanf_array(const char *buf, void *dest, const void *arg); +int ucs_config_sprintf_array(char *buf, size_t max, void *src, const void *arg); +ucs_status_t ucs_config_clone_array(void *src, void *dest, const void *arg); +void ucs_config_release_array(void *ptr, const void *arg); +void ucs_config_help_array(char *buf, size_t max, const void *arg); + +int ucs_config_sscanf_table(const char *buf, void *dest, const void *arg); +ucs_status_t ucs_config_clone_table(void *src, void *dest, const void *arg); +void ucs_config_release_table(void *ptr, const void *arg); +void ucs_config_help_table(char *buf, size_t max, const void *arg); + +void ucs_config_release_nop(void *ptr, const void *arg); +void ucs_config_help_generic(char *buf, size_t max, const void *arg); + + +#define UCS_CONFIG_DEFINE_ARRAY(_name, _elem_size, ...) \ + ucs_config_array_t ucs_array_##_name = {_elem_size, __VA_ARGS__}; + +#define UCS_CONFIG_TYPE_STRING {ucs_config_sscanf_string, ucs_config_sprintf_string, \ + ucs_config_clone_string, ucs_config_release_string, \ + ucs_config_help_generic, "string"} + +#define UCS_CONFIG_TYPE_INT {ucs_config_sscanf_int, ucs_config_sprintf_int, \ + ucs_config_clone_int, ucs_config_release_nop, \ + ucs_config_help_generic, "integer"} + +#define UCS_CONFIG_TYPE_UINT {ucs_config_sscanf_uint, ucs_config_sprintf_uint, \ + ucs_config_clone_uint, ucs_config_release_nop, \ + ucs_config_help_generic, "unsigned"} + +#define UCS_CONFIG_TYPE_ULONG {ucs_config_sscanf_ulong, ucs_config_sprintf_ulong, \ + ucs_config_clone_ulong, ucs_config_release_nop, \ + ucs_config_help_generic, "unsigned long"} + +#define UCS_CONFIG_TYPE_DOUBLE {ucs_config_sscanf_double, ucs_config_sprintf_double, \ + ucs_config_clone_double, ucs_config_release_nop, \ + ucs_config_help_generic, "floating point number"} + +#define UCS_CONFIG_TYPE_BOOL {ucs_config_sscanf_bool, ucs_config_sprintf_bool, \ + ucs_config_clone_int, ucs_config_release_nop, \ + ucs_config_help_generic, ""} + +#define UCS_CONFIG_TYPE_TERNARY {ucs_config_sscanf_ternary, ucs_config_sprintf_ternary, \ + ucs_config_clone_int, ucs_config_release_nop, \ + ucs_config_help_generic, ""} + +#define UCS_CONFIG_TYPE_ENUM(t) {ucs_config_sscanf_enum, ucs_config_sprintf_enum, \ + ucs_config_clone_uint, ucs_config_release_nop, \ + ucs_config_help_enum, t} + +#define UCS_CONFIG_TYPE_BITMAP(t) {ucs_config_sscanf_bitmap, ucs_config_sprintf_bitmap, \ + ucs_config_clone_uint, ucs_config_release_nop, \ + ucs_config_help_bitmap, t} + +#define UCS_CONFIG_TYPE_BITMASK {ucs_config_sscanf_bitmask, ucs_config_sprintf_bitmask, \ + ucs_config_clone_uint, ucs_config_release_nop, \ + ucs_config_help_generic, "bit count"} + +#define UCS_CONFIG_TYPE_PORT_SPEC {ucs_config_sscanf_port_spec, ucs_config_sprintf_port_spec, \ + ucs_config_clone_port_spec, ucs_config_release_port_spec, \ + ucs_config_help_generic, "IB port: :"} + +#define UCS_CONFIG_TYPE_TIME {ucs_config_sscanf_time, ucs_config_sprintf_time, \ + ucs_config_clone_double, ucs_config_release_nop, \ + ucs_config_help_generic, "time value: [s|us|ms|ns]"} + +#define UCS_CONFIG_TYPE_SIGNO {ucs_config_sscanf_signo, ucs_config_sprintf_signo, \ + ucs_config_clone_int, ucs_config_release_nop, \ + ucs_config_help_generic, "system signal (number or SIGxxx)"} + +#define UCS_CONFIG_TYPE_MEMUNITS {ucs_config_sscanf_memunits, ucs_config_sprintf_memunits, \ + ucs_config_clone_ulong, ucs_config_release_nop, \ + ucs_config_help_generic, "memory units: [b|kb|mb|gb], or \"inf\""} + +#define UCS_CONFIG_TYPE_ARRAY(a) {ucs_config_sscanf_array, ucs_config_sprintf_array, \ + ucs_config_clone_array, ucs_config_release_array, \ + ucs_config_help_array, &ucs_array_##a} + +#define UCS_CONFIG_TYPE_TABLE(t) {ucs_config_sscanf_table, NULL, \ + ucs_config_clone_table, ucs_config_release_table, \ + ucs_config_help_table, t} + + +/** + * Allocate and fill opts structure. + */ +ucs_status_t ucs_config_parser_read_opts(ucs_config_field_t *table, + const char *user_prefix, + size_t size, void **optsp); + +/** + * Fill existing opts structure. + */ +ucs_status_t ucs_config_parser_fill_opts(void *opts, ucs_config_field_t *table, + const char *user_prefix); + +/** + * Perform deep copy of the options structure. + */ +ucs_status_t ucs_config_parser_clone_opts(void *src, void *dst, + ucs_config_field_t *fields); + +/** + * Release the options fields. + * NOTE: Does not release the structure itself. + */ +void ucs_config_parser_release_opts(void *opts, ucs_config_field_t *fields); + +/** + * Print the options. + */ +void ucs_config_parser_print_opts(FILE *stream, const char *title, void *opts, + ucs_config_field_t *fields, unsigned flags); + +/** + * Read existing opts structure. + */ +ucs_status_t ucs_config_parser_get_value(void *opts, ucs_config_field_t *fields, + const char *name, char *value, size_t max); + +/** + * Modify existing opts structure with new setting. + */ +ucs_status_t ucs_config_parser_set_value(void *opts, ucs_config_field_t *fields, + const char *name, const char *value); + +#endif diff --git a/src/ucs/config/types.h b/src/ucs/config/types.h new file mode 100644 index 00000000000..49edcc8667d --- /dev/null +++ b/src/ucs/config/types.h @@ -0,0 +1,66 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_CONFIG_TYPES_H +#define UCS_CONFIG_TYPES_H + + +/** + * Logging levels. + */ +typedef enum { + UCS_LOG_LEVEL_FATAL, /* Immediate termination */ + UCS_LOG_LEVEL_ERROR, /* Error is returned to the user */ + UCS_LOG_LEVEL_WARN, /* Something's wrong, but we continue */ + UCS_LOG_LEVEL_INFO, /* Information */ + UCS_LOG_LEVEL_DEBUG, /* Low-volume debugging */ + UCS_LOG_LEVEL_TRACE, /* High-volume debugging */ + UCS_LOG_LEVEL_TRACE_REQ, /* Every send/receive request */ + UCS_LOG_LEVEL_TRACE_DATA, /* Data sent/received on the transport */ + UCS_LOG_LEVEL_TRACE_ASYNC, /* Asynchronous progress engine */ + UCS_LOG_LEVEL_TRACE_FUNC, /* Function calls */ + UCS_LOG_LEVEL_TRACE_POLL, /* Polling functions */ + UCS_LOG_LEVEL_LAST +} ucs_log_level_t; + + +/** + * Async progress mode. + */ +typedef enum { + UCS_ASYNC_MODE_SIGNAL, + UCS_ASYNC_MODE_THREAD, + UCS_ASYNC_MODE_POLL, /* TODO keep only in debug version */ + UCS_ASYNC_MODE_LAST +} ucs_async_mode_t; + + +/** + * Ternary logic value. + */ +typedef enum ucs_ternary_value { + UCS_NO = 0, + UCS_YES = 1, + UCS_TRY = 2, + UCS_TERNARY_LAST, +} ucs_ternary_value_t; + + +/** + * Error handling modes + */ +typedef enum { + UCS_HANDLE_ERROR_NONE, /* No error handling */ + UCS_HANDLE_ERROR_BACKTRACE, /* Print backtrace */ + UCS_HANDLE_ERROR_FREEZE, /* Freeze and wait for a debugger */ + UCS_HANDLE_ERROR_DEBUG, /* Attach debugger */ + UCS_HANDLE_ERROR_LAST +} ucs_handle_error_t; + + + +#endif /* TYPES_H_ */ diff --git a/src/ucs/datastruct/frag_list.c b/src/ucs/datastruct/frag_list.c new file mode 100644 index 00000000000..20c6befd9d5 --- /dev/null +++ b/src/ucs/datastruct/frag_list.c @@ -0,0 +1,404 @@ + +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "frag_list.h" + +#if ENABLE_STATS + +static ucs_stats_class_t ucs_frag_list_stats_class = { + .name = "frag_list", + .num_counters = UCS_FRAG_LIST_STAT_LAST, + .counter_names = { + [UCS_FRAG_LIST_STAT_GAPS] = "gaps", + [UCS_FRAG_LIST_STAT_GAP_LEN] = "gap_len", + [UCS_FRAG_LIST_STAT_GAP_OUT] = "gap_out", + [UCS_FRAG_LIST_STAT_BURSTS] = "bursts", + [UCS_FRAG_LIST_STAT_BURST_LEN] = "burst_len", + } +}; +#endif + +ucs_status_t ucs_frag_list_init(ucs_frag_list_sn_t initial_sn, ucs_frag_list_t *frag_list, + int max_holes + UCS_STATS_ARG(ucs_stats_node_t *stats_parent) + ) +{ + ucs_status_t status; + + ucs_assert(max_holes == 0 || max_holes == -1); + frag_list->head_sn = initial_sn; + frag_list->elem_count = 0; + frag_list->list_count = 0; + frag_list->max_holes = max_holes; + ucs_queue_head_init(&frag_list->list); + ucs_queue_head_init(&frag_list->ready_list); + +#if ENABLE_STATS + frag_list->prev_sn = initial_sn; +#endif + status = UCS_STATS_NODE_ALLOC(&frag_list->stats, &ucs_frag_list_stats_class, + stats_parent); + return status; +} + +void ucs_frag_list_cleanup(ucs_frag_list_t *frag_list) +{ + ucs_assert(frag_list->elem_count == 0); + ucs_assert(frag_list->list_count == 0); + ucs_assert(ucs_queue_is_empty(&frag_list->list)); + ucs_assert(ucs_queue_is_empty(&frag_list->ready_list)); + UCS_STATS_NODE_FREE(frag_list->stats); +} + +/* + prevh--- h --- .. --- + | + e + | + e + replace h with new_h: + +prevh --- new_h --- .. --- + | + h + | + e + | + e + + */ +static inline void +frag_list_replace_head(ucs_frag_list_t *frag_list, ucs_frag_list_elem_t *prevh, + ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *new_h) +{ + ucs_frag_list_elem_t UCS_V_UNUSED *e; + + ucs_trace_data("replace=%u %u", (unsigned)h->head.first_sn-1, + (unsigned)h->head.last_sn); + + new_h->head.first_sn = h->head.first_sn-1; + new_h->head.last_sn = h->head.last_sn; + /* add new_h before h in holes list */ + /* take h from holes list */ + if (prevh == NULL) { + e = ucs_queue_pull_elem_non_empty(&frag_list->list, ucs_frag_list_elem_t, list); + ucs_assert(e == h); + ucs_queue_push_head(&frag_list->list, &new_h->list); + } else { + prevh->list.next = &new_h->list; + new_h->list.next = h->list.next; + if (frag_list->list.ptail == &h->list.next) { + frag_list->list.ptail = &new_h->list.next; + } + } + + /* chain h to the new hole head */ + ucs_queue_head_init(&new_h->head.list); + ucs_queue_splice(&new_h->head.list, &h->head.list); + ucs_queue_push_head(&new_h->head.list, &h->list); +} + +/* + ..--- h --- .. --- + | + e + + add new element to h: + + ..--- h --- .. --- + | + | + e + | + elem + + */ +static inline void frag_list_add_tail(ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *elem) +{ + h->head.last_sn++; + ucs_trace_data("add_tail=%u %u", (unsigned)h->head.first_sn, (unsigned)h->head.last_sn); + + /* chain h to the new hole head */ + ucs_queue_push(&h->head.list, &elem->list); +} + +/* + merge h2 into h1. Before: + + ..--- h1 --- h2 --- + | | + e e2 + after: + ..--- h1 --- .. --- + | | + e e + | + h2 + | + e2 + */ +static inline void frag_list_merge_heads(ucs_frag_list_t *head, ucs_frag_list_elem_t *h1, ucs_frag_list_elem_t *h2) +{ + ucs_trace_data("merge_heads=%u %u", (unsigned)h1->head.first_sn, (unsigned)h2->head.last_sn); + + h1->head.last_sn = h2->head.last_sn; + h1->list.next = h2->list.next; + if (head->list.ptail == &h2->list.next) { + head->list.ptail = &h1->list.next; + } + + /* turn h2 into queue element */ + ucs_queue_push_head(&h2->head.list, &h2->list); + ucs_queue_splice(&h1->head.list, &h2->head.list); +} + +/* + insert new_h into h1. Before: + prevh--- h --- .. --- + | | + e e + | + + after: + + prevh--- new_h --- h --- ... --- + | | + e e + */ +static inline void frag_list_insert_head(ucs_frag_list_t *head, + ucs_frag_list_elem_t *prevh, ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *new_h, ucs_frag_list_sn_t sn) +{ + + ucs_trace_data("insert_head=%u prevh=%p", (unsigned)sn, prevh); + new_h->head.first_sn = new_h->head.last_sn = sn; + ucs_queue_head_init(&new_h->head.list); + + if (prevh == NULL) { + ucs_queue_push_head(&head->list, &new_h->list); + } + else { + prevh->list.next = &new_h->list; + new_h->list.next = &h->list; + } +} + + +/* + insert new_h into h1. Before: + ..--- prevh --- h --- + | | + e e + | + + after: + + ---.. --- h --- new_h + | | + e e + */ + +static inline void frag_list_insert_tail(ucs_frag_list_t *head, + ucs_frag_list_elem_t *new_h, + ucs_frag_list_sn_t sn) +{ + ucs_trace_data("insert_tail=%u", (unsigned)sn); + new_h->head.first_sn = new_h->head.last_sn = sn; + ucs_queue_head_init(&new_h->head.list); + ucs_queue_push(&head->list, &new_h->list); +} + +/** + * special case of insert where sn == head->head_sn + */ +ucs_frag_list_ooo_type_t +ucs_frag_list_insert_head(ucs_frag_list_t *head, ucs_frag_list_elem_t *elem, + ucs_frag_list_sn_t sn) +{ + ucs_frag_list_elem_t *h; + + /* next two ifs will not happen if we always pull all possible elems + * on INSERT_FIRST + */ + + /* check that we are not hitting element on the first frag list */ + if (!ucs_queue_is_empty(&head->list)) { + h = ucs_queue_head_elem_non_empty(&head->list, ucs_frag_list_elem_t, list); + if (UCS_CIRCULAR_COMPARE32(sn, >=, h->head.first_sn)) { + return UCS_FRAG_LIST_INSERT_DUP; + } + } + else { + h = NULL; + } + + head->head_sn++; + if (!ucs_queue_is_empty(&head->ready_list)) { + ucs_queue_push(&head->ready_list, &elem->list); + return UCS_FRAG_LIST_INSERT_READY; + } + + if (h != NULL && h->head.first_sn == sn + 1) { + /* do not enqueue. let know that more elems may + * be pulled from the list. + * Ex of arrivals: 2 3 1 + */ + return UCS_FRAG_LIST_INSERT_FIRST; + } + + return UCS_FRAG_LIST_INSERT_FAST; +} + +ucs_frag_list_ooo_type_t +ucs_frag_list_insert_slow(ucs_frag_list_t *head, ucs_frag_list_elem_t *elem, + ucs_frag_list_sn_t sn) +{ + ucs_frag_list_elem_t *h, *prevh, *nexth; + + if (sn == head->head_sn + 1) { + return ucs_frag_list_insert_head(head, elem, sn); + } + + if (UCS_CIRCULAR_COMPARE32(sn, <=, head->head_sn)) { + return UCS_FRAG_LIST_INSERT_DUP; + } + + if (head->max_holes == 0) { + return UCS_FRAG_LIST_INSERT_FAIL; + } + + prevh = NULL; + /* find right list to insert */ + ucs_queue_for_each(h, &head->list, list) { + /* trying to insert duplicate. retransmission or packet duplication */ + if (UCS_CIRCULAR_COMPARE32(sn, >=, h->head.first_sn) && + UCS_CIRCULAR_COMPARE32(sn, <=, h->head.last_sn)) { + return UCS_FRAG_LIST_INSERT_DUP; + } + + if (sn+1 == h->head.first_sn) { + frag_list_replace_head(head, prevh, h, elem); + /* no need to check merge here. merge iff prev->last_sn+1==sn & sn+1 == h->first_sn + * the condition is handled in next if */ + head->elem_count++; + return UCS_FRAG_LIST_INSERT_SLOW; + } + + /* todo: mark as likely */ + if (h->head.last_sn+1 == sn) { + /* add tail, check merge with next list */ + frag_list_add_tail(h, elem); + nexth = ucs_container_of(h->list.next, ucs_frag_list_elem_t, list); + + if (nexth != NULL && nexth->head.first_sn == sn + 1) { + frag_list_merge_heads(head, h, nexth); + head->list_count--; + } + head->elem_count++; + return UCS_FRAG_LIST_INSERT_SLOW; + } + + if (UCS_CIRCULAR_COMPARE32(sn, <, h->head.first_sn)) { + /* new hole, see above comment on merge */ + if (prevh) { + ucs_assert(UCS_CIRCULAR_COMPARE32(prevh->head.last_sn+1, <, sn)); + } + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAP_LEN, + prevh ? sn-prevh->head.last_sn : sn-head->head_sn); + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAPS, 1); + frag_list_insert_head(head, prevh, h, elem, sn); + head->elem_count++; + head->list_count++; + return UCS_FRAG_LIST_INSERT_SLOW; + } + + /* if we got here following must hold */ + ucs_assert(UCS_CIRCULAR_COMPARE32(h->head.last_sn+1, <, sn)); + prevh = h; + } + + frag_list_insert_tail(head, elem, sn); + + head->elem_count++; + head->list_count++; + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAP_LEN, + sn-head->head_sn); + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAPS, 1); + return UCS_FRAG_LIST_INSERT_SLOW; +} + + +/* + head->h->...-> + | + e + + * mode of action + * - check if we have elements on ready list, if we do take one from there + * - see if h is ready for extraction (sn check), extract firt, move rest to the ready list + */ + +ucs_frag_list_elem_t *ucs_frag_list_pull_slow(ucs_frag_list_t *head) +{ + ucs_frag_list_elem_t *h; + + h = ucs_queue_head_elem_non_empty(&head->list, ucs_frag_list_elem_t, list); + if (h->head.first_sn != head->head_sn+1) { + ucs_trace_data("first_sn(%u) != head_sn(%u) + 1", (unsigned)h->head.first_sn, + (unsigned)head->head_sn); + return NULL; + } + + ucs_trace_data("ready list %d to %d", (unsigned)head->head_sn, + (unsigned)h->head.last_sn); + head->head_sn = h->head.last_sn; + head->elem_count--; + head->list_count--; + + h = ucs_queue_pull_elem_non_empty(&head->list, ucs_frag_list_elem_t, list); + ucs_queue_splice(&head->ready_list, &h->head.list); + return h; +} + +void ucs_frag_list_dump(ucs_frag_list_t *head, int how) +{ + ucs_frag_list_elem_t *h, *e; + int list_count, elem_count; + int cnt; + + list_count = 0; + elem_count = 0; + + ucs_queue_for_each(e, &head->ready_list, list) { + elem_count++; + } + + ucs_queue_for_each(h, &head->list, list) { + list_count++; + cnt = 0; + ucs_queue_for_each(e, &h->head.list, list) { + cnt++; + elem_count++; + } + elem_count++; + if (how == 1) { + ucs_trace_data("%d: %d-%d %d/%d", list_count, h->head.first_sn, + h->head.last_sn, h->head.last_sn - h->head.first_sn, + cnt); + } + } + + if (how == 1) { + ucs_trace_data("elem count(expected/real)=%d/%d list_count(expected/real)=%d/%d\n", + head->elem_count, elem_count, + head->list_count, list_count); + } + + ucs_assert(head->elem_count == elem_count); + ucs_assert(head->list_count == list_count); +} + diff --git a/src/ucs/datastruct/frag_list.h b/src/ucs/datastruct/frag_list.h new file mode 100644 index 00000000000..3d631d6baa2 --- /dev/null +++ b/src/ucs/datastruct/frag_list.h @@ -0,0 +1,209 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_FRAG_LIST_H +#define UCS_FRAG_LIST_H + +#include +#include +#include +#include +#include + + +/* + * The "frag list" is a data structure containing elements ordered by sequence number. + * Elements can be added to in any order, and removed from the head (dequeued) + * in strict serial number order. + * It is used for ordering packets according to sequence number. + * + * Complexity: + * - O(1) for getting head element + * - O(Nelems) for memory, with the hard bound of sendwindowsize. In order insertion uses no memory. + * - O(k) insertion, where k is number of holes. Number of holes is expected to be + * something like SendWindowSize/BurstPacketSize. With win 1024 and burst 16 we + * get to 64 holes. In reality the number should be much less because: + * - each route send 'bursts' in order + * - it takes roughly the same time for each route + * - number of routes (burst generatos is expected to be small) + * + * so in the end number of holes is proportional to number of routes and time difference + * between alternative paths. Better math is welcome :P + * + * Organization + * + * min_sn + * head =list1-[hole]->list2-[hole]...->listn + * | | | | + * ready elemlist elemlist elemlist + * list + * + * elemlists and ready list are sorted and continuos - no holes + * ready list contains elements that can be easily pulled: head->sn = read_list.last_sn + */ + +/* Out-of-order handling type */ +typedef enum { + UCS_FRAG_LIST_INSERT_FAST, /* in order insert, list empty */ + UCS_FRAG_LIST_INSERT_FIRST, /* in order insert, list not empty, must try pull */ + UCS_FRAG_LIST_INSERT_SLOW, /* out of order insert, can not pull elems from list */ + UCS_FRAG_LIST_INSERT_DUP, /* old element, can not pull */ + UCS_FRAG_LIST_INSERT_READY, /* in order insert, while we can still pull elems from list */ + UCS_FRAG_LIST_INSERT_FAIL /* insert failed for some reason */ +} ucs_frag_list_ooo_type_t; + +/* Sequence number type */ +typedef uint32_t ucs_frag_list_sn_t; + +/* part of skb */ +typedef struct ucs_frag_list_head { + ucs_queue_head_t list; + ucs_frag_list_sn_t first_sn; + ucs_frag_list_sn_t last_sn; +} ucs_frag_list_head_t; + +typedef struct ucs_frag_list_elem_t { + ucs_queue_elem_t list; + ucs_frag_list_head_t head; +} ucs_frag_list_elem_t; + + +/* part of connection */ +typedef struct ucs_frag_list { + ucs_queue_head_t list; + ucs_queue_head_t ready_list; + ucs_frag_list_sn_t head_sn; + unsigned elem_count; /* total number of list elements */ + unsigned list_count; /* number of independent lists */ + int max_holes; /* do not allow insertion if ucs_list_count >= max_holes */ + UCS_STATS_NODE_DECLARE(stats); +#ifdef ENABLE_STATS + ucs_frag_list_sn_t prev_sn; /* needed to detect busrts */ +#endif +} ucs_frag_list_t; + +/* stat counters */ +enum { + UCS_FRAG_LIST_STAT_GAPS, + UCS_FRAG_LIST_STAT_GAP_LEN, + UCS_FRAG_LIST_STAT_GAP_OUT, + UCS_FRAG_LIST_STAT_BURSTS, + UCS_FRAG_LIST_STAT_BURST_LEN, + UCS_FRAG_LIST_STAT_LAST +}; + + +/** + * Initialize the frag_list. + * + * @param frag_list frag_list to initialize. + * @param initial_sn Sequence number to start with. This first inserted element + * should have this SN. + * @param max_holes Max number number of holes to allow on the list. + * Currently we support: + * 0 - allow no holes, only check sn. Out of order insert + * will result either in DUP or FAIL + * -1 - infinite number of holes + * + */ +ucs_status_t ucs_frag_list_init(ucs_frag_list_sn_t initial_sn, ucs_frag_list_t *frag_list, + int max_holes + UCS_STATS_ARG(ucs_stats_node_t *stats_parent)); + +/** + * Cleanup the frag_list. + */ +void ucs_frag_list_cleanup(ucs_frag_list_t *head); + + +/* Slow path insert */ +ucs_frag_list_ooo_type_t ucs_frag_list_insert_slow(ucs_frag_list_t *head, + ucs_frag_list_elem_t *elem, + ucs_frag_list_sn_t sn); + + +/** + * pull element from the list + * @return NULL if list is empty or it is impossible to pull anything + */ +ucs_frag_list_elem_t *ucs_frag_list_pull_slow(ucs_frag_list_t *head); + + +/** + * Dump frag list structure for debug purposes. + */ +void ucs_frag_list_dump(ucs_frag_list_t *head, int how); + + +static inline ucs_frag_list_sn_t ucs_frag_list_sn(ucs_frag_list_t *head) +{ + return head->head_sn; +} + +static inline void ucs_frag_list_sn_inc(ucs_frag_list_t *head) +{ + head->head_sn++; +} + +static inline unsigned ucs_frag_list_count(ucs_frag_list_t *head) +{ + return head->elem_count; +} + +static inline int ucs_frag_list_empty(ucs_frag_list_t *head) +{ + return ucs_queue_is_empty(&head->list) && ucs_queue_is_empty(&head->ready_list); +} + +static inline ucs_frag_list_ooo_type_t +ucs_frag_list_insert(ucs_frag_list_t *head, ucs_frag_list_elem_t *elem, + ucs_frag_list_sn_t sn) +{ +#if ENABLE_STATS + ucs_frag_list_ooo_type_t ret; + + if (UCS_CIRCULAR_COMPARE32(sn, >, head->head_sn)) { + if (head->prev_sn + 1 != sn) { + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_BURSTS, 1); + } else if (ucs_unlikely(UCS_STATS_GET_COUNTER(head->stats, UCS_FRAG_LIST_STAT_BURST_LEN) == 0)) { + /* initial burst */ + UCS_STATS_SET_COUNTER(head->stats, UCS_FRAG_LIST_STAT_BURSTS, 1); + } + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_BURST_LEN, 1); + head->prev_sn = sn; + } +#endif + /* in order arrival on empty list - inc sn and do nothing */ + if (ucs_likely((sn == head->head_sn + 1) && (head->elem_count == 0))) { + head->head_sn = sn; + return UCS_FRAG_LIST_INSERT_FAST; + } + + /* return either dup or slow */ +#if ENABLE_STATS + ret = ucs_frag_list_insert_slow(head, elem, sn); + UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAP_OUT, + ret != UCS_FRAG_LIST_INSERT_DUP ? head->list_count : 0); + return ret; +#else + return ucs_frag_list_insert_slow(head, elem, sn); +#endif +} + +static inline ucs_frag_list_elem_t *ucs_frag_list_pull(ucs_frag_list_t *head) +{ + if (!ucs_queue_is_empty(&head->ready_list)) { + --head->elem_count; + return ucs_queue_pull_elem_non_empty(&head->ready_list, ucs_frag_list_elem_t, list); + } else if (!ucs_queue_is_empty(&head->list)) { + return ucs_frag_list_pull_slow(head); + } else { + return NULL; + } +} + +#endif diff --git a/src/ucs/datastruct/list.h b/src/ucs/datastruct/list.h new file mode 100644 index 00000000000..c19b0202785 --- /dev/null +++ b/src/ucs/datastruct/list.h @@ -0,0 +1,177 @@ +/* +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_LIST_H_ +#define UCS_LIST_H_ + +#include + + +/** + * A link in a circular list. + */ +typedef struct ucs_list_link { + struct ucs_list_link *prev; + struct ucs_list_link *next; +} ucs_list_link_t; + + +/** + * Declare an empt list + */ +#define UCS_LIST_HEAD(name) \ + ucs_list_link_t name = { &(name), &(name) } + + +/** + * Initialize list head. + * + * @param head List head struct to initialize. + */ +static inline void ucs_list_head_init(ucs_list_link_t *head) +{ + head->prev = head->next = head; +} + +/** + * Insert an item to a list after another item. + * + * @param pos Item after which to insert. + * @param new_link Item to insert. + */ +static inline void ucs_list_insert_after(ucs_list_link_t *pos, + ucs_list_link_t *new_link) +{ + new_link->next = pos->next; + new_link->prev = pos; + pos->next->prev = new_link; + pos->next = new_link; +} + +/** + * Insert an item to a list before another item. + * + * @param pos Item before which to insert. + * @param new_link Item to insert. + */ +static inline void ucs_list_insert_before(ucs_list_link_t *pos, + ucs_list_link_t *new_link) +{ + new_link->next = pos; + new_link->prev = pos->prev; + pos->prev->next = new_link; + pos->prev = new_link; +} + +/** + * Remove an item from its list. + * + * @param link Item to remove. + */ +static inline void ucs_list_del(ucs_list_link_t *link) +{ + link->prev->next = link->next; + link->next->prev = link->prev; +} + +/** + * @return Whether the list is empty. + */ +static inline int ucs_list_is_empty(ucs_list_link_t *head) +{ + return head->next == head; +} + +/** + * Move the items from 'newlist' to the tail of the list pointed by 'head' + * + * @param head List to whose tail to add the items. + * @param newlist List of items to add. + * + * @note The contents of 'newlist' is left unmodified. + */ +static inline void ucs_list_splice_tail(ucs_list_link_t *head, + ucs_list_link_t *newlist) +{ + ucs_list_link_t *first, *last, *tail; + + if (ucs_list_is_empty(newlist)) { + return; + } + + first = newlist->next; /* First element in the new list */ + last = newlist->prev; /* Last element in the new list */ + tail = head->prev; /* Last element in the original list */ + + first->prev = tail; + tail->next = first; + + last->next = head; + head->prev = last; +} + +/** + * Count the members of the list + */ +static inline unsigned long ucs_list_length(ucs_list_link_t *head) +{ + unsigned long length; + ucs_list_link_t *ptr; + + for (ptr = head->next, length = 0; ptr != head; ptr = ptr->next, ++length); + return length; +} + +/* + * Convenience macros + */ +#define ucs_list_add_head(_head, _item) \ + ucs_list_insert_after(_head, _item) +#define ucs_list_add_tail(_head, _item) \ + ucs_list_insert_before(_head, _item) + +/** + * Get the first element in the list + */ +#define ucs_list_head(_head, _type, _member) \ + (ucs_container_of((_head)->next, _type, _member)) + +/** + * Get the last element in the list + */ +#define ucs_list_tail(_head, _type, _member) \ + (ucs_container_of((_head)->prev, _type, _member)) + +/** + * Iterate over members of the list. + */ +#define ucs_list_for_each(_elem, _head, _member) \ + for (_elem = ucs_container_of((_head)->next, typeof(*_elem), _member); \ + &(_elem)->_member != (_head); \ + _elem = ucs_container_of((_elem)->_member.next, typeof(*_elem), _member)) + +/** + * Iterate over members of the list, the user may invalidate the current entry. + */ +#define ucs_list_for_each_safe(_elem, _telem, _head, _member) \ + for (_elem = ucs_container_of((_head)->next, typeof(*_elem), _member), \ + _telem = ucs_container_of(_elem->_member.next, typeof(*_elem), _member); \ + &_elem->_member != (_head); \ + _elem = _telem, \ + _telem = ucs_container_of(_telem->_member.next, typeof(*_telem), _member)) + +/** + * Extract list head + */ +#define ucs_list_extract_head(_head, _type, _member) \ + ({ \ + ucs_list_link_t *tmp = (_head)->next; \ + ucs_list_del(tmp); \ + ucs_container_of(tmp, _type, _member); \ + }) + +#endif diff --git a/src/ucs/datastruct/mpool.c b/src/ucs/datastruct/mpool.c new file mode 100644 index 00000000000..f95c068ca5d --- /dev/null +++ b/src/ucs/datastruct/mpool.c @@ -0,0 +1,353 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "mpool.h" +#include "queue.h" + +#include +#include + +typedef struct ucs_mpool_elem { + union { + struct ucs_mpool_elem *next; /* Used when elem is in the pool */ + ucs_mpool_h mpool; /* Used when elem is outside the pool */ + }; +} ucs_mpool_elem_t; + +typedef struct ucs_mpool_chunk { + ucs_queue_elem_t queue; +} ucs_mpool_chunk_t; + + +#define ELEM_TO_OBJECT(_elem) ((void*)( (char*)_elem + sizeof(ucs_mpool_elem_t) )) +#define OBJECT_TO_ELEM(_obj) ((void*)( (char*)_obj - sizeof(ucs_mpool_elem_t) )) + +#define UCS_MPOOL_CHUNK_GROW 8 + +/* + * + * A chunk of elements looks like this: + * +-----------+-------+----------+-------+----------+------+---------+ + * | | elem0 | padding0 | elem1 | padding1 | .... | elemN-1 | + * +-----------+-------+----------+-------+----------+------+---------+ + * + * An element loooks like this: + * +------------+--------+------+ + * | mpool_elem | header | data | + * +------------+--------+------+ + * | + * This location is aligned. + */ + +struct ucs_mpool { + ucs_mpool_elem_t *freelist; + + size_t elem_size; /* Size of element in the chunk */ + size_t elem_padding; + size_t align_offset; + size_t alignment; + + unsigned num_elems; + unsigned max_elems; + unsigned elems_per_chunk; + ucs_queue_head_t chunks; +#if ENABLE_ASSERT + unsigned num_elems_inuse; +#endif + + void *mp_context; + ucs_mpool_alloc_chunk_cb alloc_chunk_cb; + ucs_mpool_free_chunk_cb free_chunk_cb; + ucs_mpool_init_obj_cb init_obj_cb; + void *init_obj_arg; + + /* Used mostly for debugging (memtrack) */ + char *name; + unsigned alloc_id; +}; + + + +static ucs_status_t ucs_mpool_allocate_chunk(ucs_mpool_h mp); + + +ucs_status_t +ucs_mpool_create(const char *name, size_t elem_size, size_t align_offset, + size_t alignment, unsigned elems_per_chunk, unsigned max_elems, + void *mp_context, + ucs_mpool_alloc_chunk_cb alloc_chunk_cb, + ucs_mpool_free_chunk_cb free_chunk_cb, + ucs_mpool_init_obj_cb init_obj_cb, void *init_obj_arg, + ucs_mpool_h *mpp) +{ + ucs_mpool_h mp; + + /* Check input values */ + if ((alignment < 1) || (elem_size == 0) || (elems_per_chunk < 1) || + (max_elems < elems_per_chunk)) + { + ucs_error("Invalid memory pool parameter(s)"); + return UCS_ERR_INVALID_PARAM; + } + + mp = ucs_malloc(sizeof *mp, "mpool context"); + if (mp == NULL) { + ucs_error("Failed to allocate memory pool"); + return UCS_ERR_NO_MEMORY; + } + + UCS_STATIC_ASSERT(UCS_MPOOL_HEADER_SIZE == sizeof(ucs_mpool_elem_t)); + + /* Initialize the pool */ + mp->freelist = NULL; + mp->alignment = alignment; + mp->elems_per_chunk = elems_per_chunk; + mp->mp_context = mp_context; + mp->alloc_chunk_cb = alloc_chunk_cb; + mp->free_chunk_cb = free_chunk_cb; + mp->init_obj_cb = init_obj_cb; + mp->init_obj_arg = init_obj_arg; + mp->name = strdup(name); +#if ENABLE_MEMTRACK + mp->alloc_id = ucs_calc_crc32(0, name, strlen(name)); +#endif + mp->num_elems = 0; + mp->max_elems = max_elems; + ucs_queue_head_init(&mp->chunks); +#if ENABLE_ASSERT + mp->num_elems_inuse = 0; +#endif + + /* Calculate element size and padding */ + mp->elem_size = sizeof(ucs_mpool_elem_t) + elem_size; + mp->align_offset = sizeof(ucs_mpool_elem_t) + align_offset; + mp->elem_padding = ucs_padding(mp->elem_size, alignment); + + VALGRIND_CREATE_MEMPOOL(mp, 0, 0); + + ucs_debug("mpool %s: align %lu, maxelems %u, elemsize %lu, padding %lu", + mp->name, mp->alignment, mp->max_elems, mp->elem_size, mp->elem_padding); + *mpp = mp; + return UCS_OK; +} + +static void __mpool_destroy(ucs_mpool_h mp, unsigned check_inuse) +{ + ucs_mpool_chunk_t *chunk; + + /* Sanity check - all elements should be put back to the mpool */ +#if ENABLE_ASSERT + if (check_inuse && mp->num_elems_inuse > 0) { + ucs_warn("destroying memory pool %s with %u used elements", mp->name, + mp->num_elems_inuse); + ucs_assert(0); + } +#endif + + while (!ucs_queue_is_empty(&mp->chunks)) { + chunk = ucs_queue_pull_elem_non_empty(&mp->chunks, ucs_mpool_chunk_t, queue); + mp->free_chunk_cb(chunk, mp->mp_context); + } + + VALGRIND_DESTROY_MEMPOOL(mp); + ucs_debug("mpool %s destroyed", mp->name); + free(mp->name); + ucs_free(mp); +} + +void ucs_mpool_destroy(ucs_mpool_h mp) +{ + __mpool_destroy(mp, 1); +} + +void ucs_mpool_destroy_unchecked(ucs_mpool_h mp) +{ + __mpool_destroy(mp, 0); +} + +void *ucs_mpool_get(ucs_mpool_h mp) +{ + ucs_mpool_elem_t *elem; + void *obj; + + if (mp->freelist == NULL && ucs_mpool_allocate_chunk(mp) != UCS_OK) { + return NULL; + } + + /* Disconnect an element from the pool */ + elem = mp->freelist; + VALGRIND_MAKE_MEM_DEFINED(elem, sizeof *elem); + mp->freelist = elem->next; + elem->mpool = mp; + VALGRIND_MAKE_MEM_NOACCESS(elem, sizeof *elem); + +#if ENABLE_ASSERT + ++mp->num_elems_inuse; + ucs_assert(mp->num_elems_inuse <= mp->num_elems); +#endif + + obj = ELEM_TO_OBJECT(elem); + VALGRIND_MEMPOOL_ALLOC(mp, obj, mp->elem_size - sizeof(ucs_mpool_elem_t)); + return obj; +} + +void ucs_mpool_put(void *obj) +{ + ucs_mpool_elem_t *elem; + ucs_mpool_h mp; + + /* Reconnect the element to the pool */ + elem = OBJECT_TO_ELEM(obj); + VALGRIND_MAKE_MEM_DEFINED(elem, sizeof *elem); + mp = elem->mpool; + elem->next = mp->freelist; + VALGRIND_MAKE_MEM_NOACCESS(elem, sizeof *elem); + mp->freelist = elem; + + VALGRIND_MEMPOOL_FREE(mp, obj); + +#if ENABLE_ASSERT + ucs_assert(mp->num_elems_inuse > 0); + --mp->num_elems_inuse; +#endif +} + +static UCS_F_NOINLINE ucs_status_t ucs_mpool_allocate_chunk(ucs_mpool_h mp) +{ + ucs_mpool_elem_t *elem, *nextelem; + int elems_in_chunk; + size_t chunk_size, chunk_padding; + ucs_mpool_chunk_t *chunk; + unsigned i; + + if (mp->num_elems >= mp->max_elems) { + return UCS_ERR_NO_MEMORY; + } + + chunk_size = sizeof(ucs_mpool_chunk_t) + mp->alignment + + mp->elems_per_chunk * (mp->elem_size + mp->elem_padding); + chunk = mp->alloc_chunk_cb(&chunk_size, mp->mp_context +#if ENABLE_MEMTRACK + , mp->name, mp->alloc_id +#endif + ); + if (chunk == NULL) { + ucs_error("Failed to allocate memory pool chunk"); + return UCS_ERR_NO_MEMORY; + } + + /* Calculate padding, and update element count according to allocated size */ + chunk_padding = ucs_padding((uintptr_t)(chunk + 1) + mp->align_offset, mp->alignment); + elems_in_chunk = (chunk_size - chunk_padding) / + (mp->elem_size + mp->elem_padding); + ucs_debug("mpool %s: allocated chunk %p of %lu bytes with %u elements", + mp->name, chunk, chunk_size, elems_in_chunk); + + nextelem = mp->freelist; + for (i = 0; i < elems_in_chunk; ++i) { + elem = (ucs_mpool_elem_t*)((char*)(chunk + 1) + chunk_padding + + i * (mp->elem_size + mp->elem_padding)); + elem->next = nextelem; + nextelem = elem; + if (mp->init_obj_cb) { + mp->init_obj_cb(ELEM_TO_OBJECT(elem), chunk, mp->mp_context, mp->init_obj_arg); + } + } + + mp->freelist = nextelem; + mp->num_elems += elems_in_chunk; + ucs_queue_push(&mp->chunks, &chunk->queue); + + VALGRIND_MAKE_MEM_NOACCESS(chunk + 1, chunk_size - sizeof(*chunk)); + return UCS_OK; +} + +void *ucs_mpool_chunk_malloc(size_t *size, void *mp_context UCS_MEMTRACK_ARG) +{ + return ucs_calloc_fwd(1, *size UCS_MEMTRACK_VAL); +} + +void ucs_mpool_chunk_free(void *chunk, void *mp_context) +{ + ucs_free(chunk); +} + +typedef struct ucs_mmap_mpool_chunk_hdr { + size_t size; +} ucs_mmap_mpool_chunk_hdr_t; + +void *ucs_mpool_chunk_mmap(size_t *size, void *mp_context UCS_MEMTRACK_ARG) +{ + ucs_mmap_mpool_chunk_hdr_t *chunk; + size_t real_size; + + real_size = ucs_align_up(*size + sizeof(*chunk), ucs_get_page_size()); + chunk = ucs_mmap_fwd(NULL, real_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 UCS_MEMTRACK_VAL); + if (chunk == MAP_FAILED) { + return NULL; + } + + chunk->size = real_size; + *size = real_size - sizeof(*chunk); + return chunk + 1; +} + +void ucs_mpool_chunk_munmap(void *ptr, void *mp_context) +{ + ucs_mmap_mpool_chunk_hdr_t *chunk = ptr; + chunk -= 1; + ucs_munmap(chunk, chunk->size); +} + +typedef struct ucs_hugetlb_mpool_chunk_hdr { + int hugetlb; +} ucs_hugetlb_mpool_chunk_hdr_t; + +void* ucs_mpool_hugetlb_malloc(size_t *size, void *mp_context UCS_MEMTRACK_ARG) +{ + ucs_hugetlb_mpool_chunk_hdr_t *chunk; + void *ptr; + ucs_status_t status; + size_t real_size; + int shmid; + + /* First, try hugetlb */ + real_size = *size; + status = ucs_sysv_alloc(&real_size, (void**)&ptr, SHM_HUGETLB, &shmid); + if (status == UCS_OK) { + chunk = ptr; + chunk->hugetlb = 1; + goto out_ok; + } + + /* Fallback to glibc */ + real_size = *size; + chunk = ucs_malloc_fwd(real_size UCS_MEMTRACK_VAL); + if (chunk != NULL) { + chunk->hugetlb = 0; + goto out_ok; + } + + return NULL; + +out_ok: + *size = real_size - sizeof(*chunk); + return chunk + 1; +} + +void ucs_mpool_hugetlb_free(void *ptr, void *mp_context) +{ + ucs_hugetlb_mpool_chunk_hdr_t *chunk; + + chunk = (ucs_hugetlb_mpool_chunk_hdr_t*)ptr - 1; + if (chunk->hugetlb) { + ucs_sysv_free(chunk); + } else { + ucs_free(chunk); + } +} diff --git a/src/ucs/datastruct/mpool.h b/src/ucs/datastruct/mpool.h new file mode 100644 index 00000000000..e06533ccf5e --- /dev/null +++ b/src/ucs/datastruct/mpool.h @@ -0,0 +1,110 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_MPOOL_H_ +#define UCS_MPOOL_H_ + +#include +#include + + +#define UCS_MPOOL_INFINITE ((unsigned)-1) +#define UCS_MPOOL_HEADER_SIZE (sizeof(void*)) + + +/** + * @param context Mempool context as passed to ucs_mpool_create(). + */ +typedef void (*ucs_mpool_non_empty_cb_t)(void *context); + +/** + * @param size Minimal size to allocate. The function may modify it to + * the actual allocated size. + * @param mp_context User-defined argument. + * @return Pointer to the allocated chunk. + */ +typedef void* (*ucs_mpool_alloc_chunk_cb)(size_t *size, void *mp_context UCS_MEMTRACK_ARG); + +/** + * @param chunk Pointer to the chunk to free. + * @param mp_context User-defined argument. + */ +typedef void (*ucs_mpool_free_chunk_cb)(void *chunk, void *mp_context); + +/** + * @param obj Object to initialize. + * @param chunk Chunk this object belongs to. + * @param mp_context User-defined argument. + * @param mp_arf User-defined argument. + */ +typedef void (*ucs_mpool_init_obj_cb)(void *obj, void *chunk, void *mp_context, void *arg); + + +typedef struct ucs_mpool *ucs_mpool_h; + +/** + * Create a memory pool, which returns elements consisting of header and data. + * The data is guaranteed to be aligned to the specified value. + * + * @param elem_size Size of an element. + * @param align_offset Offset in the element which should be aligned to the given boundary.. + * @param alignment Boundary to which align the given offset within the element. + * @param elems_per_chunk Number of elements in a single chunk. + * @param max_elems Maximal number of elements which can be allocated by the pool. + * @param mp_context Mempool context, passed to all callbacks + * @param alloc_chunk_cb Called to allocate a chunk of objects. + * @param free_chunk_cb Called to free a previously allocated chunk of objects. + * @param init_obj_cb Called to first-time initialize an object. + * @param init_obj_arg Additional rgument for init_obj_cb. + * @param mpp Upon success, filled with a handle to the memory pool. + * + * @return UCS status code. + */ +ucs_status_t +ucs_mpool_create(const char *name, size_t elem_size, size_t align_offset, + size_t alignment, unsigned elems_per_chunk, unsigned max_elems, + void *mp_context, + ucs_mpool_alloc_chunk_cb alloc_chunk_cb, + ucs_mpool_free_chunk_cb free_chunk_cb, + ucs_mpool_init_obj_cb init_obj_cb, void *init_obj_arg, + ucs_mpool_h *mpp); + +void ucs_mpool_destroy(ucs_mpool_h mp); +void ucs_mpool_destroy_unchecked(ucs_mpool_h mp); + +/** + * Get an element from the memory pool. + */ +void *ucs_mpool_get(ucs_mpool_h mp); + + +/** + * Return an element to its memory pool. + */ +void ucs_mpool_put(void *obj); + +/** + * Simple chunk allocator (default). + */ +void *ucs_mpool_chunk_malloc(size_t *size, void *mp_context UCS_MEMTRACK_ARG); +void ucs_mpool_chunk_free(void *chunk, void *mp_context); + + +/* + * mmap chunk allocator. + */ +void *ucs_mpool_chunk_mmap(size_t *size, void *mp_context UCS_MEMTRACK_ARG); +void ucs_mpool_chunk_munmap(void *chunk, void *mp_context); + +/** + * Hugetlb chunk allocator. + */ +void* ucs_mpool_hugetlb_malloc(size_t *size, void *mp_context UCS_MEMTRACK_ARG); +void ucs_mpool_hugetlb_free(void *ptr, void *mp_context); + + +#endif /* MPOOL_H_ */ diff --git a/src/ucs/datastruct/ptr_array.c b/src/ucs/datastruct/ptr_array.c new file mode 100644 index 00000000000..8b66031055f --- /dev/null +++ b/src/ucs/datastruct/ptr_array.c @@ -0,0 +1,192 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "ptr_array.h" + +#include +#include +#include + + +/* Initial allocation size */ +#define UCS_PTR_ARRAY_INITIAL_SIZE 8 + + +static inline int ucs_ptr_array_is_free(ucs_ptr_array_t *ptr_array, unsigned index) +{ + return (index < ptr_array->size) && + __ucs_ptr_array_is_free(ptr_array->start[index]); +} + +static inline uint32_t ucs_ptr_array_placeholder_get(ucs_ptr_array_elem_t elem) +{ + ucs_assert(__ucs_ptr_array_is_free(elem)); + return elem >> UCS_PTR_ARRAY_PLCHDR_SHIFT; +} + +static inline void ucs_ptr_array_placeholder_set(ucs_ptr_array_elem_t *elem, + uint32_t placeholder) +{ + *elem = (*elem & ~UCS_PTR_ARRAY_PLCHDR_MASK) | + (((ucs_ptr_array_elem_t)placeholder) << UCS_PTR_ARRAY_PLCHDR_SHIFT); +} + +static inline unsigned +ucs_ptr_array_freelist_get_next(ucs_ptr_array_elem_t elem) +{ + ucs_assert(__ucs_ptr_array_is_free(elem)); + return (elem & UCS_PTR_ARRAY_NEXT_MASK) >> UCS_PTR_ARRAY_NEXT_SHIFT; +} + +static inline void +ucs_ptr_array_freelist_set_next(ucs_ptr_array_elem_t *elem, unsigned next) +{ + ucs_assert(next <= UCS_PTR_ARRAY_NEXT_MASK); + *elem = (*elem & ~UCS_PTR_ARRAY_NEXT_MASK) | + (((ucs_ptr_array_elem_t)next) << UCS_PTR_ARRAY_NEXT_SHIFT); +} + +static void UCS_F_MAYBE_UNUSED ucs_ptr_array_dump(ucs_ptr_array_t *ptr_array) +{ +#if ENABLE_ASSERT + ucs_ptr_array_elem_t elem; + unsigned i; + + ucs_trace_data("ptr_array start %p size %u", ptr_array->start, ptr_array->size); + for (i = 0; i < ptr_array->size; ++i) { + elem = ptr_array->start[i]; + if (ucs_ptr_array_is_free(ptr_array, i)) { + ucs_trace_data("[%u]= (%u)", i, + ucs_ptr_array_placeholder_get(elem)); + } else { + ucs_trace_data("[%u]=%p", i, (void*)elem); + } + } + + ucs_trace_data("freelist:"); + i = ptr_array->freelist; + while (i != UCS_PTR_ARRAY_SENTINEL) { + ucs_trace_data("[%u] %p", i, &ptr_array->start[i]); + i = ucs_ptr_array_freelist_get_next(ptr_array->start[i]); + } +#endif +} + +static void ucs_ptr_array_clear(ucs_ptr_array_t *ptr_array) +{ + ptr_array->start = NULL; + ptr_array->size = 0; + ptr_array->freelist = UCS_PTR_ARRAY_SENTINEL; +} + +void ucs_ptr_array_init(ucs_ptr_array_t *ptr_array, uint32_t init_placeholder) +{ + ptr_array->init_placeholder = init_placeholder; + ucs_ptr_array_clear(ptr_array); +} + +void ucs_ptr_array_cleanup(ucs_ptr_array_t *ptr_array) +{ + unsigned i, inuse; + + inuse = 0; + for (i = 0; i < ptr_array->size; ++i) { + if (!ucs_ptr_array_is_free(ptr_array, i)) { + ++inuse; + } + } + + if (inuse > 0) { + ucs_warn("releasing ptr_array with %u used items", inuse); + } + + ucs_free(ptr_array->start); + ucs_ptr_array_clear(ptr_array); +} + +static void ucs_ptr_array_grow(ucs_ptr_array_t *ptr_array UCS_MEMTRACK_ARG) +{ + ucs_ptr_array_elem_t *new_array; + unsigned curr_size, new_size; + unsigned i, next; + + curr_size = ptr_array->size; + if (curr_size == 0) { + new_size = UCS_PTR_ARRAY_INITIAL_SIZE; + } else { + new_size = curr_size * 2; + } + + /* Allocate new array */ + new_array = ucs_malloc_fwd(new_size * sizeof(ucs_ptr_array_elem_t) UCS_MEMTRACK_VAL); + ucs_assert_always(new_array != NULL); + memcpy(new_array, ptr_array->start, curr_size * sizeof(ucs_ptr_array_elem_t)); + + /* Link all new array items */ + for (i = curr_size; i < new_size; ++i) { + new_array[i] = UCS_PTR_ARRAY_FLAG_FREE; + ucs_ptr_array_placeholder_set(&new_array[i], ptr_array->init_placeholder); + ucs_ptr_array_freelist_set_next(&new_array[i], i + 1); + } + ucs_ptr_array_freelist_set_next(&new_array[new_size - 1], UCS_PTR_ARRAY_SENTINEL); + + /* Find last free list element */ + if (ptr_array->freelist == UCS_PTR_ARRAY_SENTINEL) { + ptr_array->freelist = curr_size; + } else { + next = ptr_array->freelist; + do { + i = next; + next = ucs_ptr_array_freelist_get_next(ptr_array->start[i]); + } while (next != UCS_PTR_ARRAY_SENTINEL); + ucs_ptr_array_freelist_set_next(&ptr_array->start[i], curr_size); + } + + /* Switch to new array */ + ucs_free(ptr_array->start); + ptr_array->start = new_array; + ptr_array->size = new_size; +} + +unsigned ucs_ptr_array_insert(ucs_ptr_array_t *ptr_array, void *value, + uint32_t *placeholder_p UCS_MEMTRACK_ARG) +{ + ucs_ptr_array_elem_t *elem; + unsigned index; + + ucs_assert_always(((uintptr_t)value & UCS_PTR_ARRAY_FLAG_FREE) == 0); + + if (ptr_array->freelist == UCS_PTR_ARRAY_SENTINEL) { + ucs_ptr_array_grow(ptr_array UCS_MEMTRACK_VAL); + } + + /* Get the first item on the free list */ + index = ptr_array->freelist; + ucs_assert(index != UCS_PTR_ARRAY_SENTINEL); + elem = &ptr_array->start[index]; + + /* Remove from free list */ + ptr_array->freelist = ucs_ptr_array_freelist_get_next(*elem); + + /* Populate */ + *placeholder_p = ucs_ptr_array_placeholder_get(*elem); + *elem = (uintptr_t)value; + return index; +} + +void ucs_ptr_array_remove(ucs_ptr_array_t *ptr_array, unsigned index, + uint32_t placeholder) +{ + ucs_ptr_array_elem_t *elem = &ptr_array->start[index]; + + ucs_assert_always(!ucs_ptr_array_is_free(ptr_array, index)); + *elem = UCS_PTR_ARRAY_FLAG_FREE; + ucs_ptr_array_placeholder_set(elem, placeholder); + ucs_ptr_array_freelist_set_next(elem, ptr_array->freelist); + ptr_array->freelist = index; +} + diff --git a/src/ucs/datastruct/ptr_array.h b/src/ucs/datastruct/ptr_array.h new file mode 100644 index 00000000000..f109488679d --- /dev/null +++ b/src/ucs/datastruct/ptr_array.h @@ -0,0 +1,120 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef PTR_ARRAY_H_ +#define PTR_ARRAY_H_ + +#include +#include + + +/* + * Array element layout: + * + * 64 32 1 0 + * +-----------------+----------------+---+ + * free: | placeholder | next index | 1 | + * +-----------------+----------------+---+ + * used: | user pointer | 0 | + * +-----------------+----------------+---+ + * + * + */ +typedef uint64_t ucs_ptr_array_elem_t; + + +/** + * A sparse array of pointers. + * Free slots can hold 32-bit placeholder value. + */ +typedef struct ucs_ptr_array { + uint32_t init_placeholder; + ucs_ptr_array_elem_t *start; + unsigned freelist; + unsigned size; +} ucs_ptr_array_t; + + +/* Flags added to lower bits of the value */ +#define UCS_PTR_ARRAY_FLAG_FREE ((unsigned long)0x01) /* Slot is free */ + +#define UCS_PTR_ARRAY_PLCHDR_SHIFT 32 +#define UCS_PTR_ARRAY_PLCHDR_MASK (((ucs_ptr_array_elem_t)-1) & ~UCS_MASK(UCS_PTR_ARRAY_PLCHDR_SHIFT)) +#define UCS_PTR_ARRAY_NEXT_SHIFT 1 +#define UCS_PTR_ARRAY_NEXT_MASK (UCS_MASK(UCS_PTR_ARRAY_PLCHDR_SHIFT) & ~UCS_MASK(UCS_PTR_ARRAY_NEXT_SHIFT)) +#define UCS_PTR_ARRAY_SENTINEL (UCS_PTR_ARRAY_NEXT_MASK >> UCS_PTR_ARRAY_NEXT_SHIFT) + +#define __ucs_ptr_array_is_free(_elem) \ + ((uintptr_t)(_elem) & UCS_PTR_ARRAY_FLAG_FREE) + + +/** + * Initialize the array. + * + * @param init_placeholder Default placeholder value. + */ +void ucs_ptr_array_init(ucs_ptr_array_t *ptr_array, uint32_t init_placeholder); + + +/** + * Cleanup the array. + * All values should already be removed from it. + */ +void ucs_ptr_array_cleanup(ucs_ptr_array_t *ptr_array); + + +/** + * Insert a pointer to the array. + * + * @param value Pointer to insert. Must be 8-byte aligned. + * @param placeholder Filled with placeholder value. + * @return The index to which the value was inserted. + * + * Complexity: amortized O(1) + * + * Note: The array will grow if needed. + */ +unsigned ucs_ptr_array_insert(ucs_ptr_array_t *ptr_array, void *value, + uint32_t *placeholder_p UCS_MEMTRACK_ARG); + + +/** + * Remove a pointer from the array. + * + * @param index Index to remove from. + * @param placeholder Value to put in the free slot. + * + * Complexity: O(1) + */ +void ucs_ptr_array_remove(ucs_ptr_array_t *ptr_array, unsigned index, + uint32_t placeholder); + + +/** + * Retrieve a value from the array. + * + * @param index Index to retrieve the value from. + * @param value Filled with the value. + * @return Whether the value is present and valid. + * + * Complexity: O(1) + */ +#define ucs_ptr_array_lookup(_ptr_array, _index, _var) \ + (((_index) >= (_ptr_array)->size) ? \ + (UCS_V_INITIALIZED(_var), 0) : \ + !__ucs_ptr_array_is_free(_var = (void*)((_ptr_array)->start[_index]))) + + +/** + * Iterate over all valid elements in the array. + */ +#define ucs_ptr_array_for_each(_var, _index, _ptr_array) \ + for (_index = 0; _index < (_ptr_array)->size; ++_index) \ + if (!__ucs_ptr_array_is_free(_var = (void*)((_ptr_array)->start[_index]))) \ + + +#endif /* PTR_ARRAY_H_ */ diff --git a/src/ucs/datastruct/queue.h b/src/ucs/datastruct/queue.h new file mode 100644 index 00000000000..800e3635ead --- /dev/null +++ b/src/ucs/datastruct/queue.h @@ -0,0 +1,253 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_QUEUE_H_ +#define UCS_QUEUE_H_ + +#include +#include + +typedef struct ucs_queue_elem ucs_queue_elem_t; +typedef struct ucs_queue_head ucs_queue_head_t; +typedef ucs_queue_elem_t** ucs_queue_iter_t; + +/** + * Queue element type. + */ +struct ucs_queue_elem { + ucs_queue_elem_t *next; +}; + + +/** + * Queue type. + */ +struct ucs_queue_head { + ucs_queue_elem_t *head; + ucs_queue_elem_t **ptail; +}; + + +/** + * Initialize a queue. + * + * @param queue Queue to initialize. + */ +static inline void ucs_queue_head_init(ucs_queue_head_t *queue) +{ + queue->ptail = &queue->head; +} + +/** + * @return Queue length. + */ +static inline size_t ucs_queue_length(ucs_queue_head_t *queue) +{ + ucs_queue_elem_t **pelem; + size_t length; + + length = 0; + for (pelem = &queue->head; pelem != queue->ptail; pelem = &(*pelem)->next) { + ++length; + } + return length; +} + +/** + * @return Whether the queue is empty. + */ +static inline int ucs_queue_is_empty(ucs_queue_head_t *queue) +{ + return queue->ptail == &queue->head; +} + +/** + * Enqueue an element to the tail of the queue. + * + * @param queue Queue to add to. + * @param elem Element to add. + */ +static inline void ucs_queue_push(ucs_queue_head_t *queue, ucs_queue_elem_t *elem) +{ + *queue->ptail = elem; + queue->ptail = &elem->next; +#if ENABLE_ASSERT + elem->next = NULL; /* For sanity check below */ +#endif +} + +/** + * Add an element to the head of the queue. + * + * @param queue Queue to add to. + * @param elem Element to add. + */ +static inline void ucs_queue_push_head(ucs_queue_head_t *queue, + ucs_queue_elem_t *elem) +{ + elem->next = queue->head; + queue->head = elem; + if (queue->ptail == &queue->head) { + queue->ptail = &elem->next; + } +} + +/** + * Dequeue an element from the head of the queue, assuming the queue is not empty. + * + * @param queue Non-empty queue to pull from. + * @return Element from the head of the queue. + */ +static inline ucs_queue_elem_t *ucs_queue_pull_non_empty(ucs_queue_head_t *queue) +{ + ucs_queue_elem_t *elem; + + elem = queue->head; + queue->head = elem->next; + if (queue->ptail == &elem->next) { + queue->ptail = &queue->head; + } + return elem; +} + +/** + * Delete an element. + * After the call, iter points to the next element. + */ +static inline void ucs_queue_del_iter(ucs_queue_head_t *queue, ucs_queue_iter_t iter) +{ + if (queue->ptail == &(*iter)->next) { + queue->ptail = iter; /* deleting the last element */ + } + *iter = (*iter)->next; + + /* Sanity check */ + ucs_assertv((queue->head != NULL) || (queue->ptail == &queue->head), + "head=%p ptail=%p iter=%p", queue->head, queue->ptail, iter); +} + +/** + * Dequeue an element from the head of the queue. + * + * @param queue Queue to pull from. + * @return Element from the head of the queue, or NULL if the queue is empty. + */ +static inline ucs_queue_elem_t *ucs_queue_pull(ucs_queue_head_t *queue) +{ + if (ucs_queue_is_empty(queue)) + return NULL; + return ucs_queue_pull_non_empty(queue); +} + +/** + * Insert all elements from one queue to another queue, leaving the first queue + * empty. + * + * @param queue Queue to push elements to. + * @param new_elems Queue of elements to add. + */ +static inline void ucs_queue_splice(ucs_queue_head_t *queue, + ucs_queue_head_t *new_elems) +{ + if (!ucs_queue_is_empty(new_elems)) { + *queue->ptail = new_elems->head; + queue->ptail = new_elems->ptail; + new_elems->ptail = &new_elems->head; + } +} + +/** + * Convenience macro to pull from a non-empty queue and return the containing element. + * + * @param queue Non-empty queue to pull from. + * @param type Container element type. + * @param member Queue element member inside the container. + * + * @return Pulled element. + */ +#define ucs_queue_pull_elem_non_empty(queue, type, member) \ + ucs_container_of(ucs_queue_pull_non_empty(queue), type, member) + +/** + * Convenience macro to get the head element of a non-empty queue. + * + * @param queue Non-empty queue whose head element to get. + * @param type Container element type. + * @param member Queue element member inside the container. + * + * @return Head element. + */ +#define ucs_queue_head_elem_non_empty(queue, type, member) \ + ucs_container_of((queue)->head, type, member) + +/** + * Iterate over queue elements. The queue must not be modified during the iteration. + * + * @param elem Variable which will hold point to the element in the queue. + * @param queue Queue to iterate on. + * @param member Member inside 'elem' which is the queue link. + */ +#define ucs_queue_for_each(elem, queue, member) \ + for (*(queue)->ptail = NULL, \ + elem = ucs_container_of((queue)->head, typeof(*elem), member); \ + &elem->member != NULL; \ + elem = ucs_container_of(elem->member.next, typeof(*elem), member)) + +/** + * Iterate over queue elements. The current element may be safely removed from + * the queue using ucs_queue_del_iter(). + * + * @param elem Variable which will hold point to the element in the queue. + * @param iter Iterator variable. May be passed to ucs_queue_del_iter(). + * @param queue Queue to iterate on. + * @param member Member inside 'elem' which is the queue link. + */ +#define ucs_queue_for_each_safe(elem, iter, queue, member) \ + for (*(queue)->ptail = NULL, \ + iter = &(queue)->head, \ + elem = ucs_container_of(*iter, typeof(*elem), member); \ + iter != (queue)->ptail; \ + iter = (*iter == &elem->member) ? &(*iter)->next : iter, \ + elem = ucs_container_of(*iter, typeof(*elem), member)) + + +/* + * Queue iteration + */ + +static inline ucs_queue_iter_t ucs_queue_iter_begin(ucs_queue_head_t *q) +{ + return &q->head; +} + +static inline ucs_queue_iter_t ucs_queue_iter_next(ucs_queue_iter_t i) +{ + return &(*i)->next; +} + +static inline int ucs_queue_iter_end(ucs_queue_head_t *q, ucs_queue_iter_t i) +{ + return i == q->ptail; +} + +static inline void ucs_queue_remove(ucs_queue_head_t *queue, ucs_queue_elem_t *elem) +{ + ucs_queue_iter_t iter = ucs_queue_iter_begin(queue); + + while (!ucs_queue_iter_end(queue, iter)) { + if (*iter == elem) { + ucs_queue_del_iter(queue, iter); + return; + } + iter = ucs_queue_iter_next(iter); + } +} + +#define ucs_queue_iter_elem(elem, iter, member) \ + ucs_container_of(*iter, typeof(*elem), member) + +#endif diff --git a/src/ucs/datastruct/sglib.h b/src/ucs/datastruct/sglib.h new file mode 100644 index 00000000000..1a4780fa64e --- /dev/null +++ b/src/ucs/datastruct/sglib.h @@ -0,0 +1,1952 @@ +/* + + This is SGLIB version 1.0.3 + + (C) by Marian Vittek, Bratislava, http://www.xref-tech.com/sglib, 2003-5 + + License Conditions: You can use a verbatim copy (including this + copyright notice) of sglib freely in any project, commercial or not. + You can also use derivative forms freely under terms of Open Source + Software license or under terms of GNU Public License. If you need + to use a derivative form in a commercial project, or you need sglib + under any other license conditions, contact the author. + + + +*/ + + +#ifndef _SGLIB__h_ +#define _SGLIB__h_ + +/* the assert is used exclusively to write unexpected error messages */ +#include + + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ +/* - LEVEL - 0 INTERFACE - */ +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------------- */ +/* ------------------------------ STATIC ARRAYS ------------------------------- */ +/* ---------------------------------------------------------------------------- */ + +/* + + Basic algorithms for sorting arrays. Multiple depending arrays can + be rearranged using user defined 'elem_exchangers' + +*/ + +/* HEAP - SORT (level 0) */ + +#define SGLIB_ARRAY_SINGLE_HEAP_SORT(type, a, max, comparator) {\ + SGLIB_ARRAY_HEAP_SORT(type, a, max, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ +} + +#define SGLIB_ARRAY_HEAP_SORT(type, a, max, comparator, elem_exchanger) {\ + int _k_;\ + for(_k_=(max)/2; _k_>=0; _k_--) {\ + SGLIB___ARRAY_HEAP_DOWN(type, a, _k_, max, comparator, elem_exchanger);\ + }\ + for(_k_=(max)-1; _k_>=0; _k_--) {\ + elem_exchanger(type, a, 0, _k_);\ + SGLIB___ARRAY_HEAP_DOWN(type, a, 0, _k_, comparator, elem_exchanger);\ + }\ +} + +#define SGLIB___ARRAY_HEAP_DOWN(type, a, ind, max, comparator, elem_exchanger) {\ + type _t_;\ + int _m_, _l_, _r_, _i_;\ + _i_ = (ind);\ + _m_ = _i_;\ + do {\ + _i_ = _m_; \ + _l_ = 2*_i_+1;\ + _r_ = _l_+1;\ + if (_l_ < (max)){\ + if (comparator(((a)[_m_]), ((a)[_l_])) < 0) _m_ = _l_;\ + if (_r_ < (max)) {\ + if (comparator(((a)[_m_]), ((a)[_r_])) < 0) _m_ = _r_;\ + }\ + }\ + if (_m_ != _i_) {\ + elem_exchanger(type, a, _i_, _m_);\ + }\ + } while (_m_ != _i_);\ +} + + +/* QUICK - SORT (level 0) */ + +#define SGLIB_ARRAY_SINGLE_QUICK_SORT(type, a, max, comparator) {\ + SGLIB_ARRAY_QUICK_SORT(type, a, max, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ +} + +#define SGLIB_ARRAY_QUICK_SORT(type, a, max, comparator, elem_exchanger) {\ + int _i_, _j_, _p_, _stacki_, _start_, _end_;\ + /* can sort up to 2^64 elements */\ + int _startStack_[64]; \ + int _endStack_[64];\ + type _tmp_;\ + _startStack_[0] = 0;\ + _endStack_[0] = (max);\ + _stacki_ = 1;\ + while (_stacki_ > 0) {\ + _stacki_ --;\ + _start_ = _startStack_[_stacki_];\ + _end_ = _endStack_[_stacki_];\ + while (_end_ - _start_ > 2) {\ + _p_ = _start_;\ + _i_ = _start_ + 1;\ + _j_ = _end_ - 1;\ + while (_i_<_j_) {\ + for(; _i_<=_j_ && comparator(((a)[_i_]),((a)[_p_]))<=0; _i_++) ;\ + if (_i_ > _j_) {\ + /* all remaining elements lesseq than pivot */\ + elem_exchanger(type, a, _j_, _p_);\ + _i_ = _j_;\ + } else {\ + for(; _i_<=_j_ && comparator(((a)[_j_]),((a)[_p_]))>=0; _j_--) ;\ + if (_i_ > _j_) {\ + /* all remaining elements greater than pivot */\ + elem_exchanger(type, a, _j_, _p_);\ + _i_ = _j_;\ + } else if (_i_ < _j_) {\ + elem_exchanger(type, a, _i_, _j_);\ + if (_i_+2 < _j_) {_i_++; _j_--;}\ + else if (_i_+1 < _j_) _i_++;\ + }\ + }\ + }\ + /* O.K. i==j and pivot is on a[i] == a[j] */\ + /* handle recursive calls without recursion */\ + if (_i_-_start_ > 1 && _end_-_j_ > 1) {\ + /* two recursive calls, use array-stack */\ + if (_i_-_start_ < _end_-_j_-1) {\ + _startStack_[_stacki_] = _j_+1;\ + _endStack_[_stacki_] = _end_;\ + _stacki_ ++;\ + _end_ = _i_;\ + } else {\ + _startStack_[_stacki_] = _start_;\ + _endStack_[_stacki_] = _i_;\ + _stacki_ ++;\ + _start_ = _j_+1;\ + }\ + } else {\ + if (_i_-_start_ > 1) {\ + _end_ = _i_;\ + } else {\ + _start_ = _j_+1;\ + }\ + }\ + }\ + if (_end_ - _start_ == 2) {\ + if (comparator(((a)[_start_]),((a)[_end_-1])) > 0) {\ + elem_exchanger(type, a, _start_, _end_-1);\ + }\ + }\ + }\ +} + +/* BINARY SEARCH (level 0) */ + +#define SGLIB_ARRAY_BINARY_SEARCH(type, a, start_index, end_index, key, comparator, found, result_index) {\ + int _kk_, _cc_, _ii_, _jj_, _ff_;\ + _ii_ = (start_index); \ + _jj_ = (end_index);\ + _ff_ = 0;\ + while (_ii_ <= _jj_ && _ff_==0) {\ + _kk_ = (_jj_+_ii_)/2;\ + _cc_ = comparator(((a)[_kk_]), (key));\ + if (_cc_ == 0) {\ + (result_index) = _kk_; \ + _ff_ = 1;\ + } else if (_cc_ < 0) {\ + _ii_ = _kk_+1;\ + } else {\ + _jj_ = _kk_-1;\ + }\ + }\ + if (_ff_ == 0) {\ + /* not found, but set its resulting place in the array */\ + (result_index) = _jj_+1;\ + }\ + (found) = _ff_;\ +} + +/* -------------------------------- queue (in an array) ------------------ */ +/* queue is a quadruple (a,i,j,dim) such that: */ +/* a is the array storing values */ +/* i is the index of the first used element in the array */ +/* j is the index of the first free element in the array */ +/* dim is the size of the array a */ +/* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ + +#define SGLIB_QUEUE_INIT(type, a, i, j) { i = j = 0; } +#define SGLIB_QUEUE_IS_EMPTY(type, a, i, j) ((i)==(j)) +#define SGLIB_QUEUE_IS_FULL(type, a, i, j, dim) ((i)==((j)+1)%(dim)) +#define SGLIB_QUEUE_FIRST_ELEMENT(type, a, i, j) (a[i]) +#define SGLIB_QUEUE_ADD_NEXT(type, a, i, j, dim) {\ + if (SGLIB_QUEUE_IS_FULL(type, a, i, j, dim)) assert(0 && "the queue is full");\ + (j) = ((j)+1) % (dim);\ +} +#define SGLIB_QUEUE_ADD(type, a, elem, i, j, dim) {\ + a[j] = (elem);\ + SGLIB_QUEUE_ADD_NEXT(type, a, i, j, dim);\ +} +#define SGLIB_QUEUE_DELETE_FIRST(type, a, i, j, dim) {\ + if (SGLIB_QUEUE_IS_EMPTY(type, a, i, j)) assert(0 && "the queue is empty");\ + (i) = ((i)+1) % (dim);\ +} +#define SGLIB_QUEUE_DELETE(type, a, i, j, dim) {\ + SGLIB_QUEUE_DELETE_FIRST(type, a, i, j, dim);\ +} + +/* ----------------- priority queue (heap) (in an array) -------------------- */ +/* heap is a triple (a,i,dim) such that: */ +/* a is the array storing values */ +/* i is the index of the first free element in the array */ +/* dim is the size of the array a */ +/* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ + +#define SGLIB_HEAP_INIT(type, a, i) { i = 0; } +#define SGLIB_HEAP_IS_EMPTY(type, a, i) ((i)==0) +#define SGLIB_HEAP_IS_FULL(type, a, i, dim) ((i)==(dim)) +#define SGLIB_HEAP_FIRST_ELEMENT(type, a, i) (a[0]) +#define SGLIB_HEAP_ADD_NEXT(type, a, i, dim, comparator, elem_exchanger) {\ + int _i_;\ + if (SGLIB_HEAP_IS_FULL(type, a, i, dim)) assert(0 && "the heap is full");\ + _i_ = (i)++;\ + while (_i_ > 0 && comparator(a[_i_/2], a[_i_]) < 0) {\ + elem_exchanger(type, a, (_i_/2), _i_);\ + _i_ = _i_/2;\ + }\ +} +#define SGLIB_HEAP_ADD(type, a, elem, i, dim, comparator) {\ + if (SGLIB_HEAP_IS_FULL(type, a, i, dim)) assert(0 && "the heap is full");\ + a[i] = (elem);\ + SGLIB_HEAP_ADD_NEXT(type, a, i, dim, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ +} +#define SGLIB_HEAP_DELETE_FIRST(type, a, i, dim, comparator, elem_exchanger) {\ + if (SGLIB_HEAP_IS_EMPTY(type, a, i)) assert(0 && "the heap is empty");\ + (i)--;\ + a[0] = a[i];\ + SGLIB___ARRAY_HEAP_DOWN(type, a, 0, i, comparator, elem_exchanger);\ +} +#define SGLIB_HEAP_DELETE(type, a, i, dim, comparator) {\ + SGLIB_HEAP_DELETE_FIRST(type, a, i, dim, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ +} + + +/* ----------------- hashed table of pointers (in an array) -------------------- */ + +/* + + This hashed table is storing pointers to objects (not containers). + In this table there is a one-to-one mapping between 'objects' stored + in the table and indexes where they are placed. Each index is + pointing to exactly one 'object' and each 'object' stored in the + table occurs on exactly one index. Once an object is stored in the + table, it can be represented via its index. + + In case of collision while adding an object the index shifted + by SGLIB_HASH_TAB_SHIFT_CONSTANT (constant can be redefined) + + You can NOT delete an element from such hash table. The only + justification (I can see) for this data structure is an exchange + file format, having an index table at the beginning and then + refering objects via indexes. + + !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! + +*/ + +#define SGLIB_HASH_TAB_INIT(type, table, dim) {\ + int _i_;\ + for(_i_ = 0; _i_ < (dim); _i_++) (table)[_i_] = NULL;\ +} + +#define SGLIB_HASH_TAB_ADD_IF_NOT_MEMBER(type, table, dim, elem, hash_function, comparator, member){\ + unsigned _pos_;\ + type *_elem_;\ + SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, _pos_, _elem_);\ + (member) = (table)[_pos_];\ + if (_elem_ == NULL) {\ + if ((table)[_pos_] != NULL) assert(0 && "the hash table is full");\ + (table)[_pos_] = (elem);\ + }\ +} + +#define SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, hash_function, comparator, resultIndex, resultMember) {\ + unsigned _i_;\ + int _count_;\ + type *_e_;\ + _count = 0;\ + _i_ = hash_function(elem);\ + _i_ %= (dim);\ + while ((_e_=(table)[_i_])!=NULL && comparator(_e_, (elem))!=0 && _count_<(dim)) {\ + _count_ ++;\ + _i_ = (_i_ + SGLIB_HASH_TAB_SHIFT_CONSTANT) % (dim);\ + }\ + (resultIndex) = _i_;\ + if (_count_ < (dim)) (resultMember) = _e_;\ + else (resultMember) = NULL;\ +} + +#define SGLIB_HASH_TAB_IS_MEMBER(type, table, dim, elem, hash_function, resultIndex) {\ + unsigned _i_;\ + int _c_;\ + type *_e_;\ + _count = 0;\ + _i_ = hash_function(elem);\ + _i_ %= (dim);\ + while ((_e_=(table)[_i_])!=NULL && _e_!=(elem) && _c_<(dim)) {\ + _c_ ++;\ + _i_ = (_i_ + SGLIB_HASH_TAB_SHIFT_CONSTANT) % (dim);\ + }\ + if (_e_==(elem)) (resultIndex) = _i_;\ + else (resultIndex) = -1;\ +} + +#define SGLIB_HASH_TAB_MAP_ON_ELEMENTS(type, table, dim, iteratedIndex, iteratedVariable, command) {\ + unsigned iteratedIndex;\ + type *iteratedVariable;\ + for(iteratedIndex=0; iteratedIndex < (dim); iteratedIndex++) {\ + iteratedVariable = (table)[iteratedIndex];\ + if (iteratedVariable != NULL) {command;}\ + }\ +} + + +/* ---------------------------------------------------------------------------- */ +/* ------------------------- DYNAMIC DATA STRUCTURES -------------------------- */ +/* ---------------------------------------------------------------------------- */ + +/* ------------------------------------ lists (level 0) --------------------- */ + +#define SGLIB_LIST_ADD(type, list, elem, next) {\ + (elem)->next = (list);\ + (list) = (elem);\ +} + +#define SGLIB_LIST_CONCAT(type, first, second, next) {\ + if ((first)==NULL) {\ + (first) = (second);\ + } else {\ + type *_p_;\ + for(_p_ = (first); _p_->next!=NULL; _p_=_p_->next) ;\ + _p_->next = (second);\ + }\ +} + +#define SGLIB_LIST_DELETE(type, list, elem, next) {\ + type **_p_;\ + for(_p_ = &(list); *_p_!=NULL && *_p_!=(elem); _p_= &(*_p_)->next) ;\ + assert(*_p_!=NULL && "element is not member of the container, use DELETE_IF_MEMBER instead"!=NULL);\ + *_p_ = (*_p_)->next;\ +} + +#define SGLIB_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, next, member) {\ + type *_p_;\ + for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) != 0; _p_= _p_->next) ;\ + (member) = _p_;\ + if (_p_ == NULL) {\ + SGLIB_LIST_ADD(type, list, elem, next);\ + }\ +} + +#define SGLIB_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, next, member) {\ + type **_p_;\ + for(_p_ = &(list); *_p_!=NULL && comparator((*_p_), (elem)) != 0; _p_= &(*_p_)->next) ;\ + (member) = *_p_;\ + if (*_p_ != NULL) {\ + *_p_ = (*_p_)->next;\ + }\ +} + +#define SGLIB_LIST_IS_MEMBER(type, list, elem, next, result) {\ + type *_p_;\ + for(_p_ = (list); _p_!=NULL && _p_ != (elem); _p_= _p_->next) ;\ + (result) = (_p_!=NULL);\ +} + +#define SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, next, member) {\ + type *_p_;\ + for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) != 0; _p_= _p_->next) ;\ + (member) = _p_;\ +} + +#define SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command) {\ + type *_ne_;\ + type *iteratedVariable;\ + (iteratedVariable) = (list); \ + while ((iteratedVariable)!=NULL) {\ + _ne_ = (iteratedVariable)->next;\ + {command;};\ + (iteratedVariable) = _ne_;\ + }\ +} + +#define SGLIB_LIST_LEN(type, list, next, result) {\ + type *_ce_;\ + (result) = 0;\ + SGLIB_LIST_MAP_ON_ELEMENTS(type, list, _ce_, next, (result)++);\ +} + +#define SGLIB_LIST_REVERSE(type, list, next) {\ + type *_list_,*_tmp_,*_res_;\ + _list_ = (list);\ + _res_ = NULL;\ + while (_list_!=NULL) {\ + _tmp_ = _list_->next; _list_->next = _res_;\ + _res_ = _list_; _list_ = _tmp_;\ + }\ + (list) = _res_;\ +} + +#define SGLIB_LIST_SORT(type, list, comparator, next) {\ + /* a non-recursive merge sort on lists */\ + type *_r_;\ + type *_a_, *_b_, *_todo_, *_t_, **_restail_;\ + int _i_, _n_, _contFlag_;\ + _r_ = (list);\ + _contFlag_ = 1;\ + for(_n_ = 1; _contFlag_; _n_ = _n_+_n_) {\ + _todo_ = _r_; _r_ = NULL; _restail_ = &_r_; _contFlag_ =0;\ + while (_todo_!=NULL) {\ + _a_ = _todo_;\ + for(_i_ = 1, _t_ = _a_; _i_ < _n_ && _t_!=NULL; _i_++, _t_ = _t_->next) ;\ + if (_t_ ==NULL) {\ + *_restail_ = _a_;\ + break;\ + }\ + _b_ = _t_->next; _t_->next=NULL;\ + for(_i_ =1, _t_ = _b_; _i_<_n_ && _t_!=NULL; _i_++, _t_ = _t_->next) ;\ + if (_t_ ==NULL) {\ + _todo_ =NULL;\ + } else {\ + _todo_ = _t_->next; _t_->next=NULL;\ + }\ + /* merge */\ + while (_a_!=NULL && _b_!=NULL) {\ + if (comparator(_a_, _b_) < 0) {\ + *_restail_ = _a_; _restail_ = &(_a_->next); _a_ = _a_->next;\ + } else {\ + *_restail_ = _b_; _restail_ = &(_b_->next); _b_ = _b_->next;\ + }\ + }\ + if (_a_!=NULL) *_restail_ = _a_;\ + else *_restail_ = _b_;\ + while (*_restail_!=NULL) _restail_ = &((*_restail_)->next);\ + _contFlag_ =1;\ + }\ + }\ + (list) = _r_;\ +} + +/* --------------------------------- sorted list (level 0) --------------------- */ +/* + All operations suppose that the list is sorted and they preserve + this property. +*/ + + +#define SGLIB_SORTED_LIST_ADD(type, list, elem, comparator, next) {\ + type **_e_;\ + int _cmpres_;\ + SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmpres_, _e_);\ + (elem)->next = *_e_;\ + *_e_ = (elem);\ +} + +#define SGLIB_SORTED_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, next, member) {\ + type **_e_;\ + int _cmp_res_;\ + SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmp_res_, _e_);\ + if (_cmp_res_ != 0) {\ + (elem)->next = *_e_;\ + *_e_ = (elem);\ + (member) = NULL;\ + } else {\ + (member) = *_e_;\ + }\ +} + +#define SGLIB_SORTED_LIST_DELETE(type, list, elem, next) {\ + SGLIB_LIST_DELETE(type, list, elem, next);\ +} + +#define SGLIB_SORTED_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, next, member) {\ + type **_e_;\ + int _cmp_res_;\ + SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmp_res_, _e_);\ + if (_cmp_res_ == 0) {\ + (member) = *_e_;\ + *_e_ = (*_e_)->next;\ + } else {\ + (member) = NULL;\ + }\ +} + +#define SGLIB_SORTED_LIST_FIND_MEMBER(type, list, elem, comparator, next, member) {\ + type *_p_;\ + int _cmpres_ = 1;\ + for(_p_ = (list); _p_!=NULL && (_cmpres_=comparator(_p_, (elem))) < 0; _p_=_p_->next) ;\ + if (_cmpres_ != 0) (member) = NULL;\ + else (member) = _p_;\ +} + +#define SGLIB_SORTED_LIST_IS_MEMBER(type, list, elem, comparator, next, result) {\ + type *_p_;\ + for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) < 0; _p_=_p_->next) ;\ + while (_p_ != NULL && _p_ != (elem) && comparator(_p_, (elem)) == 0) _p_=_p_->next;\ + (result) = (_p_ == (elem));\ +} + +#define SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, comparator_result, member_ptr) {\ + (comparator_result) = -1;\ + for((member_ptr) = &(list); \ + *(member_ptr)!=NULL && ((comparator_result)=comparator((*member_ptr), (elem))) < 0; \ + (member_ptr) = &(*(member_ptr))->next) ;\ +} + +#define SGLIB_SORTED_LIST_LEN(type, list, next, result) {\ + SGLIB_LIST_LEN(type, list, next, result);\ +} + +#define SGLIB_SORTED_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command) {\ + SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command);\ +} + + +/* ------------------------------- double linked list (level 0) ------------------------- */ +/* + Lists with back pointer to previous element. Those lists implements deletion + of an element in a constant time. +*/ + +#define SGLIB___DL_LIST_CREATE_SINGLETON(type, list, elem, previous, next) {\ + (list) = (elem);\ + (list)->next = (list)->previous = NULL;\ +} + +#define SGLIB_DL_LIST_ADD_AFTER(type, place, elem, previous, next) {\ + if ((place) == NULL) {\ + SGLIB___DL_LIST_CREATE_SINGLETON(type, place, elem, previous, next);\ + } else {\ + (elem)->next = (place)->next;\ + (elem)->previous = (place);\ + (place)->next = (elem);\ + if ((elem)->next != NULL) (elem)->next->previous = (elem);\ + }\ +} + +#define SGLIB_DL_LIST_ADD_BEFORE(type, place, elem, previous, next) {\ + if ((place) == NULL) {\ + SGLIB___DL_LIST_CREATE_SINGLETON(type, place, elem, previous, next);\ + } else {\ + (elem)->next = (place);\ + (elem)->previous = (place)->previous;\ + (place)->previous = (elem);\ + if ((elem)->previous != NULL) (elem)->previous->next = (elem);\ + }\ +} + +#define SGLIB_DL_LIST_ADD(type, list, elem, previous, next) {\ + SGLIB_DL_LIST_ADD_BEFORE(type, list, elem, previous, next)\ +} + +#define SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, the_add_operation) {\ + type *_dlp_;\ + for(_dlp_ = (list); _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->previous) ;\ + if (_dlp_ == NULL && (list) != NULL) {\ + for(_dlp_ = (list)->next; _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->next) ;\ + }\ + (member) = _dlp_;\ + if (_dlp_ == NULL) {\ + the_add_operation(type, list, elem, previous, next);\ + }\ +} + +#define SGLIB_DL_LIST_ADD_BEFORE_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ + SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD_BEFORE);\ +} + +#define SGLIB_DL_LIST_ADD_AFTER_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ + SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD_AFTER);\ +} + +#define SGLIB_DL_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ + SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD);\ +} + +#define SGLIB_DL_LIST_CONCAT(type, first, second, previous, next) {\ + if ((first)==NULL) {\ + (first) = (second);\ + } else if ((second)!=NULL) {\ + type *_dlp_;\ + for(_dlp_ = (first); _dlp_->next!=NULL; _dlp_=_dlp_->next) ;\ + SGLIB_DL_LIST_ADD_AFTER(type, _dlp_, second, previous, next);\ + }\ +} + +#define SGLIB_DL_LIST_DELETE(type, list, elem, previous, next) {\ + type *_l_;\ + _l_ = (list);\ + if (_l_ == (elem)) {\ + if ((elem)->previous != NULL) _l_ = (elem)->previous;\ + else _l_ = (elem)->next;\ + }\ + if ((elem)->next != NULL) (elem)->next->previous = (elem)->previous;\ + if ((elem)->previous != NULL) (elem)->previous->next = (elem)->next;\ + (list) = _l_;\ +} + +#define SGLIB_DL_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, previous, next, member) {\ + type *_dlp_;\ + for(_dlp_ = (list); _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->previous) ;\ + if (_dlp_ == NULL && (list) != NULL) {\ + for(_dlp_ = (list)->next; _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->next) ;\ + }\ + (member) = _dlp_;\ + if (_dlp_ != NULL) {\ + SGLIB_DL_LIST_DELETE(type, list, _dlp_, previous, next);\ + }\ +} + +#define SGLIB_DL_LIST_IS_MEMBER(type, list, elem, previous, next, result) {\ + type *_dlp_;\ + SGLIB_LIST_IS_MEMBER(type, list, elem, previous, result);\ + if (result == 0 && (list) != NULL) {\ + _dlp_ = (list)->next;\ + SGLIB_LIST_IS_MEMBER(type, _dlp_, elem, next, result);\ + }\ +} + +#define SGLIB_DL_LIST_FIND_MEMBER(type, list, elem, comparator, previous, next, member) {\ + type *_dlp_;\ + SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, previous, member);\ + if ((member) == NULL && (list) != NULL) {\ + _dlp_ = (list)->next;\ + SGLIB_LIST_FIND_MEMBER(type, _dlp_, elem, comparator, next, member);\ + }\ +} + +#define SGLIB_DL_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, previous, next, command) {\ + type *_dl_;\ + type *iteratedVariable;\ + if ((list)!=NULL) {\ + _dl_ = (list)->next;\ + SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, previous, command);\ + SGLIB_LIST_MAP_ON_ELEMENTS(type, _dl_, iteratedVariable, next, command);\ + }\ +} + +#define SGLIB_DL_LIST_SORT(type, list, comparator, previous, next) {\ + type *_dll_, *_dlp_, *_dlt_;\ + _dll_ = (list);\ + if (_dll_ != NULL) {\ + for(; _dll_->previous!=NULL; _dll_=_dll_->previous) ;\ + SGLIB_LIST_SORT(type, _dll_, comparator, next);\ + SGLIB___DL_LIST_CREATE_FROM_LIST(type, _dll_, previous, next);\ + (list) = _dll_;\ + }\ +} + +#define SGLIB_DL_LIST_GET_FIRST(type, list, previous, next, result) {\ + type *_dll_;\ + _dll_ = (list);\ + if (_dll_ != NULL) {\ + for(; _dll_->previous!=NULL; _dll_=_dll_->previous) ;\ + }\ + (result) = _dll_;\ +} + +#define SGLIB_DL_LIST_GET_LAST(type, list, previous, next, result) {\ + type *_dll_;\ + _dll_ = (list);\ + if (_dll_ != NULL) {\ + for(; _dll_->next!=NULL; _dll_=_dll_->next) ;\ + }\ + (result) = _dll_;\ +} + +#define SGLIB_DL_LIST_LEN(type, list, previous, next, result) {\ + type *_dl_;\ + int _r1_, _r2_;\ + if ((list)==NULL) {\ + (result) = 0;\ + } else {\ + SGLIB_LIST_LEN(type, list, previous, _r1_);\ + _dl_ = (list)->next;\ + SGLIB_LIST_LEN(type, _dl_, next, _r2_);\ + (result) = _r1_ + _r2_;\ + }\ +} + +#define SGLIB_DL_LIST_REVERSE(type, list, previous, next) {\ + type *_list_,*_nlist_,*_dlp_,*_dln_;\ + _list_ = (list);\ + if (_list_!=NULL) {\ + _nlist_ = _list_->next;\ + while (_list_!=NULL) {\ + _dln_ = _list_->next; \ + _dlp_ = _list_->previous; \ + _list_->next = _dlp_;\ + _list_->previous = _dln_;\ + _list_ = _dlp_;\ + }\ + while (_nlist_!=NULL) {\ + _dln_ = _nlist_->next; \ + _dlp_ = _nlist_->previous; \ + _nlist_->next = _dlp_;\ + _nlist_->previous = _dln_;\ + _nlist_ = _dln_;\ + }\ + }\ +} + +#define SGLIB___DL_LIST_CREATE_FROM_LIST(type, list, previous, next) {\ + type *_dlp_, *_dlt_;\ + _dlp_ = NULL;\ + for(_dlt_ = (list); _dlt_!=NULL; _dlt_ = _dlt_->next) {\ + _dlt_->previous = _dlp_;\ + _dlp_ = _dlt_;\ + }\ +} + + +/* ------------------------------- binary tree traversal (level 0) -------------------- */ + + +#define SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, iteratedVariable, order, left, right, command) {\ + /* this is non-recursive implementation of tree traversal */\ + /* it maintains the path to the current node in the array '_path_' */\ + /* the _path_[0] contains the root of the tree; */\ + /* the _path_[_pathi_] contains the _current_element_ */\ + /* the macro does not use the _current_element_ after execution of command */\ + /* command can destroy it, it can free the element for example */\ + type *_path_[SGLIB_MAX_TREE_DEEP];\ + type *_right_[SGLIB_MAX_TREE_DEEP];\ + char _pass_[SGLIB_MAX_TREE_DEEP];\ + type *_cn_;\ + int _pathi_;\ + type *iteratedVariable;\ + _cn_ = (tree);\ + _pathi_ = 0;\ + while (_cn_!=NULL) {\ + /* push down to leftmost innermost element */\ + while(_cn_!=NULL) {\ + _path_[_pathi_] = _cn_;\ + _right_[_pathi_] = _cn_->right;\ + _pass_[_pathi_] = 0;\ + _cn_ = _cn_->left;\ + if (order == 0) {\ + iteratedVariable = _path_[_pathi_];\ + {command;}\ + }\ + _pathi_ ++;\ + if (_pathi_ >= SGLIB_MAX_TREE_DEEP) assert(0 && "the binary_tree is too deep");\ + }\ + do {\ + _pathi_ --;\ + if ((order==1 && _pass_[_pathi_] == 0)\ + || (order == 2 && (_pass_[_pathi_] == 1 || _right_[_pathi_]==NULL))) {\ + iteratedVariable = _path_[_pathi_];\ + {command;}\ + }\ + _pass_[_pathi_] ++;\ + } while (_pathi_>0 && _right_[_pathi_]==NULL) ;\ + _cn_ = _right_[_pathi_];\ + _right_[_pathi_] = NULL;\ + _pathi_ ++;\ + }\ +} + +#define SGLIB_BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, left, right, command) {\ + SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 1, left, right, command);\ +} + +#define SGLIB_BIN_TREE_MAP_ON_ELEMENTS_PREORDER(type, tree, _current_element_, left, right, command) {\ + SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 0, left, right, command);\ +} + +#define SGLIB_BIN_TREE_MAP_ON_ELEMENTS_POSTORDER(type, tree, _current_element_, left, right, command) {\ + SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 2, left, right, command);\ +} + +#define SGLIB___BIN_TREE_FIND_MEMBER(type, tree, elem, left, right, comparator, res) {\ + type *_s_;\ + int _c_;\ + _s_ = (tree);\ + while (_s_!=NULL) {\ + _c_ = comparator((elem), _s_);\ + if (_c_ < 0) _s_ = _s_->left;\ + else if (_c_ > 0) _s_ = _s_->right;\ + else break;\ + }\ + (res) = _s_;\ +} + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ +/* - LEVEL - 1 INTERFACE - */ +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + + + +/* ---------------------------------------------------------------------------- */ +/* ------------------------------ STATIC ARRAYS ------------------------------- */ +/* ---------------------------------------------------------------------------- */ + +/* ----------------------------- array sorting (level 1) ---------------------- */ + +#define SGLIB_DEFINE_ARRAY_SORTING_PROTOTYPES(type, comparator) \ + extern void sglib_##type##_array_quick_sort(type *a, int max);\ + extern void sglib_##type##_array_heap_sort(type *a, int max);\ + + +#define SGLIB_DEFINE_ARRAY_SORTING_FUNCTIONS(type, comparator) \ + void sglib_##type##_array_quick_sort(type *a, int max) {\ + SGLIB_ARRAY_SINGLE_QUICK_SORT(type, a, max, comparator);\ + }\ + void sglib_##type##_array_heap_sort(type *a, int max) {\ + SGLIB_ARRAY_SINGLE_HEAP_SORT(type, a, max, comparator);\ + }\ + + +/* ----------------------------- array queue (level 1) ------------------- */ +/* sglib's queue is stored in a fixed sized array */ +/* queue_type MUST be a structure containing fields: */ +/* afield is the array storing elem_type */ +/* ifield is the index of the first element in the queue */ +/* jfield is the index of the first free element after the queue */ +/* dim is the size of the array afield */ +/* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ + + +#define SGLIB_DEFINE_QUEUE_PROTOTYPES(queue_type, elem_type, afield, ifield, jfield, dim) \ + extern void sglib_##queue_type##_init(queue_type *q); \ + extern int sglib_##queue_type##_is_empty(queue_type *q); \ + extern int sglib_##queue_type##_is_full(queue_type *q); \ + extern elem_type sglib_##queue_type##_first_element(queue_type *q); \ + extern elem_type *sglib_##queue_type##_first_element_ptr(queue_type *q); \ + extern void sglib_##queue_type##_add_next(queue_type *q); \ + extern void sglib_##queue_type##_add(queue_type *q, elem_type elem); \ + extern void sglib_##queue_type##_delete_first(queue_type *q); \ + extern void sglib_##queue_type##_delete(queue_type *q); + + +#define SGLIB_DEFINE_QUEUE_FUNCTIONS(queue_type, elem_type, afield, ifield, jfield, dim) \ + void sglib_##queue_type##_init(queue_type *q) {\ + SGLIB_QUEUE_INIT(elem_type, q->afield, q->ifield, q->jfield);\ + }\ + int sglib_##queue_type##_is_empty(queue_type *q) {\ + return(SGLIB_QUEUE_IS_EMPTY(elem_type, q->afield, q->ifield, q->jfield));\ + }\ + int sglib_##queue_type##_is_full(queue_type *q) {\ + return(SGLIB_QUEUE_IS_FULL(elem_type, q->afield, q->ifield, q->jfield));\ + }\ + elem_type sglib_##queue_type##_first_element(queue_type *q) {\ + return(SGLIB_QUEUE_FIRST_ELEMENT(elem_type, q->afield, q->ifield, q->jfield));\ + }\ + elem_type *sglib_##queue_type##_first_element_ptr(queue_type *q) {\ + return(& SGLIB_QUEUE_FIRST_ELEMENT(elem_type, q->afield, q->ifield, q->jfield));\ + }\ + void sglib_##queue_type##_add_next(queue_type *q) {\ + SGLIB_QUEUE_ADD_NEXT(elem_type, q->afield, q->ifield, q->jfield, dim);\ + }\ + void sglib_##queue_type##_add(queue_type *q, elem_type elem) {\ + SGLIB_QUEUE_ADD(elem_type, q->afield, elem, q->ifield, q->jfield, dim);\ + }\ + void sglib_##queue_type##_delete_first(queue_type *q) {\ + SGLIB_QUEUE_DELETE_FIRST(elem_type, q->afield, q->ifield, q->jfield, dim);\ + }\ + void sglib_##queue_type##_delete(queue_type *q) {\ + SGLIB_QUEUE_DELETE_FIRST(elem_type, q->afield, q->ifield, q->jfield, dim);\ + } + + +/* ------------------------ array heap (level 1) ------------------------- */ +/* sglib's heap is a priority queue implemented in a fixed sized array */ +/* heap_type MUST be a structure containing fields: */ +/* afield is the array of size dim storing elem_type */ +/* ifield is the index of the first free element after the queue */ +/* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ + + +#define SGLIB_DEFINE_HEAP_PROTOTYPES(heap_type, elem_type, afield, ifield, dim, comparator, elem_exchanger) \ + extern void sglib_##heap_type##_init(heap_type *q); \ + extern int sglib_##heap_type##_is_empty(heap_type *q); \ + extern int sglib_##heap_type##_is_full(heap_type *q); \ + extern elem_type sglib_##heap_type##_first_element(heap_type *q); \ + extern elem_type *sglib_##heap_type##_first_element_ptr(heap_type *q); \ + extern void sglib_##heap_type##_add_next(heap_type *q); \ + extern void sglib_##heap_type##_add(heap_type *q, elem_type elem); \ + extern void sglib_##heap_type##_delete_first(heap_type *q); \ + extern void sglib_##heap_type##_delete(heap_type *q) + +#define SGLIB_DEFINE_HEAP_FUNCTIONS(heap_type, elem_type, afield, ifield, dim, comparator, elem_exchanger) \ + void sglib_##heap_type##_init(heap_type *q) {\ + SGLIB_HEAP_INIT(elem_type, q->afield, q->ifield);\ + }\ + int sglib_##heap_type##_is_empty(heap_type *q) {\ + return(SGLIB_HEAP_IS_EMPTY(elem_type, q->afield, q->ifield));\ + }\ + int sglib_##heap_type##_is_full(heap_type *q) {\ + return(SGLIB_HEAP_IS_FULL(elem_type, q->afield, q->ifield));\ + }\ + elem_type sglib_##heap_type##_first_element(heap_type *q) {\ + return(SGLIB_HEAP_FIRST_ELEMENT(elem_type, q->afield, q->ifield));\ + }\ + elem_type *sglib_##heap_type##_first_element_ptr(heap_type *q) {\ + return(& SGLIB_HEAP_FIRST_ELEMENT(elem_type, q->afield, q->ifield));\ + }\ + void sglib_##heap_type##_add_next(heap_type *q) {\ + SGLIB_HEAP_ADD_NEXT(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ + }\ + void sglib_##heap_type##_add(heap_type *q, elem_type elem) {\ + SGLIB_HEAP_ADD(elem_type, q->afield, elem, q->ifield, dim, comparator, elem_exchanger);\ + }\ + void sglib_##heap_type##_delete_first(heap_type *q) {\ + SGLIB_HEAP_DELETE_FIRST(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ + }\ + void sglib_##heap_type##_delete(heap_type *q) {\ + SGLIB_HEAP_DELETE_FIRST(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ + } + + +/* ------------------------ hashed table (level 1) ------------------------- */ +/* + + sglib's hash table is an array storing directly pointers to objects (not containers). + In this table there is a one-to-one mapping between 'objects' stored + in the table and indexes where they are placed. Each index is + pointing to exactly one 'object' and each 'object' stored in the + table occurs on exactly one index. Once an object is stored in the + table, it can be represented via its index. + + type - is the type of elements + dim - is the size of the hash array + hash_function - is a hashing function mapping type* to unsigned + comparator - is a comparator on elements + + !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! +*/ + +#define SGLIB_DEFINE_HASHED_TABLE_PROTOTYPES(type, dim, hash_function, comparator) \ + struct sglib_hashed_##type##_iterator {\ + int currentIndex;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + extern void sglib_hashed_##type##_init(type *table[dim]);\ + extern int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member);\ + extern int sglib_hashed_##type##_is_member(type *table[dim], type *elem);\ + extern type * sglib_hashed_##type##_find_member(type *table[dim], type *elem);\ + extern type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]); \ + extern type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it); \ + extern type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it); + +#define SGLIB_DEFINE_HASHED_TABLE_FUNCTIONS(type, dim, hash_function, comparator) \ + struct sglib_hashed_##type##_iterator {\ + int currentIndex;\ + type **table;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + void sglib_hashed_##type##_init(type *table[dim]) {\ + SGLIB_HASH_TAB_INIT(type, table, dim);\ + }\ + int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member) {\ + SGLIB_HASH_TAB_ADD_IF_NOT_MEMBER(type, table, dim, elem, hash_function, comparator, *member);\ + }\ + int sglib_hashed_##type##_is_member(type *table[dim], type *elem) {\ + int ind;\ + SGLIB_HASH_TAB_IS_MEMBER(type, table, dim, elem, hash_function, ind);\ + return(ind != -1);\ + }\ + type * sglib_hashed_##type##_find_member(type *table[dim], type *elem) {\ + type *mmb;\ + int ind;\ + SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, hash_function, comparator, ind, mmb);\ + return(mmb);\ + }\ + type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto) {\ + int i;\ + it->table = table;\ + it->subcomparator = subcomparator;\ + it->equalto = equalto;\ + for(i=0; i<(dim) && table[i]==NULL; i++) ;\ + it->currentIndex = i;\ + if (i<(dim)) return(table[i]);\ + return(NULL);\ + }\ + type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]) {\ + sglib_hashed_##type##_it_init_on_equal(it, table, NULL, NULL);\ + }\ + type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it) {\ + return(table[it->currentIndex]);\ + }\ + type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it) {\ + i=it->currentIndex;\ + if (i<(dim)) {\ + for(i++; i<(dim) && table[i]==NULL; i++) ;\ + }\ + it->currentIndex = i;\ + if (i<(dim)) return(table[i]);\ + return(NULL);\ + } + + +/* ------------------- hashed container (only for level 1) -------------------- */ +/* + hashed container is a table of given fixed size containing another + (dynamic) base container in each cell. Once an object should be + inserted into the hashed container, a hash function is used to + determine the cell where the object belongs and the object is + inserted into the base container stored in this cell. Usually the + base container is simply a list or a sorted list, but it can be a + red-black tree as well. + + parameters: + type - the type of the container stored in each cell. + dim - the size of the hashed array + hash_function - the hashing function hashing 'type *' to unsigned. + +*/ + +#define SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(type, dim, hash_function) \ + struct sglib_hashed_##type##_iterator {\ + struct sglib_##type##_iterator containerIt;\ + type **table;\ + int currentIndex;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + extern void sglib_hashed_##type##_init(type *table[dim]);\ + extern void sglib_hashed_##type##_add(type *table[dim], type *elem);\ + extern int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member);\ + extern void sglib_hashed_##type##_delete(type *table[dim], type *elem);\ + extern int sglib_hashed_##type##_delete_if_member(type *table[dim], type *elem, type **memb);\ + extern int sglib_hashed_##type##_is_member(type *table[dim], type *elem);\ + extern type * sglib_hashed_##type##_find_member(type *table[dim], type *elem);\ + extern type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]); \ + extern type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it); \ + extern type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it); + +#define SGLIB_DEFINE_HASHED_CONTAINER_FUNCTIONS(type, dim, hash_function) \ + /*extern unsigned hash_function(type *elem);*/\ + void sglib_hashed_##type##_init(type *table[dim]) {\ + unsigned i;\ + for(i=0; i<(dim); i++) table[i] = NULL;\ + }\ + void sglib_hashed_##type##_add(type *table[dim], type *elem) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + sglib_##type##_add(&(table)[i], elem);\ + }\ + int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + return(sglib_##type##_add_if_not_member(&(table)[i], elem, member));\ + }\ + void sglib_hashed_##type##_delete(type *table[dim], type *elem) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + sglib_##type##_delete(&(table)[i], elem);\ + }\ + int sglib_hashed_##type##_delete_if_member(type *table[dim], type *elem, type **memb) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + return(sglib_##type##_delete_if_member(&(table)[i], elem, memb));\ + }\ + int sglib_hashed_##type##_is_member(type *table[dim], type *elem) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + return(sglib_##type##_is_member((table)[i], elem));\ + }\ + type * sglib_hashed_##type##_find_member(type *table[dim], type *elem) {\ + unsigned i;\ + i = ((unsigned)hash_function(elem)) % (dim);\ + return(sglib_##type##_find_member((table)[i], elem));\ + }\ + type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto) {\ + type *e;\ + it->table = table;\ + it->currentIndex = 0;\ + it->subcomparator = subcomparator;\ + it->equalto = equalto;\ + e = sglib_##type##_it_init_on_equal(&it->containerIt, table[it->currentIndex], it->subcomparator, it->equalto);\ + if (e==NULL) e = sglib_hashed_##type##_it_next(it);\ + return(e);\ + }\ + type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]) {\ + return(sglib_hashed_##type##_it_init_on_equal(it, table, NULL, NULL));\ + }\ + type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it) {\ + return(sglib_##type##_it_current(&it->containerIt));\ + }\ + type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it) {\ + type *e;\ + e = sglib_##type##_it_next(&it->containerIt);\ + while (e==NULL && (++(it->currentIndex))<(dim)) {\ + e = sglib_##type##_it_init_on_equal(&it->containerIt, it->table[it->currentIndex], it->subcomparator, it->equalto);\ + }\ + return(e);\ + } + + + +/* ---------------------------------------------------------------------------- */ +/* ------------------------- DYNAMIC DATA STRUCTURES -------------------------- */ +/* ---------------------------------------------------------------------------- */ + + + +/* ------------------------------------ list (level 1) -------------------------------- */ + +#define SGLIB_DEFINE_LIST_PROTOTYPES(type, comparator, next) \ + struct sglib_##type##_iterator {\ + type *currentelem;\ + type *nextelem;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + extern void sglib_##type##_add(type **list, type *elem);\ + extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ + extern void sglib_##type##_concat(type **first, type *second);\ + extern void sglib_##type##_delete(type **list, type *elem);\ + extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ + extern int sglib_##type##_is_member(type *list, type *elem);\ + extern type *sglib_##type##_find_member(type *list, type *elem);\ + extern void sglib_##type##_sort(type **list);\ + extern int sglib_##type##_len(type *list);\ + extern void sglib_##type##_reverse(type **list);\ + extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ + extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ + extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); + + +#define SGLIB_DEFINE_LIST_FUNCTIONS(type, comparator, next) \ + int sglib_##type##_is_member(type *list, type *elem) {\ + int result;\ + SGLIB_LIST_IS_MEMBER(type, list, elem, next, result);\ + return(result);\ + }\ + type *sglib_##type##_find_member(type *list, type *elem) {\ + type *result;\ + SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, next, result);\ + return(result);\ + }\ + int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ + SGLIB_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, next, *member);\ + return(*member==NULL);\ + }\ + void sglib_##type##_add(type **list, type *elem) {\ + SGLIB_LIST_ADD(type, *list, elem, next);\ + }\ + void sglib_##type##_concat(type **first, type *second) {\ + SGLIB_LIST_CONCAT(type, *first, second, next);\ + }\ + void sglib_##type##_delete(type **list, type *elem) {\ + SGLIB_LIST_DELETE(type, *list, elem, next);\ + }\ + int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ + SGLIB_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, next, *member);\ + return(*member!=NULL);\ + }\ + void sglib_##type##_sort(type **list) { \ + SGLIB_LIST_SORT(type, *list, comparator, next);\ + }\ + int sglib_##type##_len(type *list) {\ + int res;\ + SGLIB_LIST_LEN(type, list, next, res);\ + return(res);\ + }\ + void sglib_##type##_reverse(type **list) {\ + SGLIB_LIST_REVERSE(type, *list, next);\ + }\ + \ + type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ + it->subcomparator = subcomparator;\ + it->equalto = equalto;\ + it->nextelem = list;\ + return(sglib_##type##_it_next(it));\ + }\ + type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ + return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ + }\ + type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ + return(it->currentelem);\ + }\ + type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ + type *ce, *eq;\ + int (*scp)(type *, type *);\ + ce = it->nextelem;\ + it->nextelem = NULL;\ + if (it->subcomparator != NULL) {\ + eq = it->equalto; \ + scp = it->subcomparator;\ + while (ce!=NULL && scp(ce, eq)!=0) ce = ce->next;\ + }\ + it->currentelem = ce;\ + if (ce != NULL) it->nextelem = ce->next;\ + return(ce);\ + } + +/* ----------------------------- sorted list (level 1) ----------------------------------- */ + + +#define SGLIB_DEFINE_SORTED_LIST_PROTOTYPES(type, comparator, next) \ + struct sglib_##type##_iterator {\ + type *currentelem;\ + type *nextelem;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + extern void sglib_##type##_add(type **list, type *elem);\ + extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ + extern void sglib_##type##_delete(type **list, type *elem);\ + extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ + extern int sglib_##type##_is_member(type *list, type *elem);\ + extern type *sglib_##type##_find_member(type *list, type *elem);\ + extern int sglib_##type##_len(type *list);\ + extern void sglib_##type##_sort(type **list);\ + extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ + extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ + extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); + + +#define SGLIB_DEFINE_SORTED_LIST_FUNCTIONS(type, comparator, next) \ + int sglib_##type##_is_member(type *list, type *elem) {\ + int result;\ + SGLIB_SORTED_LIST_IS_MEMBER(type, list, elem, comparator, next, result);\ + return(result);\ + }\ + type *sglib_##type##_find_member(type *list, type *elem) {\ + type *result;\ + SGLIB_SORTED_LIST_FIND_MEMBER(type, list, elem, comparator, next, result);\ + return(result);\ + }\ + int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ + SGLIB_SORTED_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, next, *member);\ + return(*member==NULL);\ + }\ + void sglib_##type##_add(type **list, type *elem) {\ + SGLIB_SORTED_LIST_ADD(type, *list, elem, comparator, next);\ + }\ + void sglib_##type##_delete(type **list, type *elem) {\ + SGLIB_SORTED_LIST_DELETE(type, *list, elem, next);\ + }\ + int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ + SGLIB_SORTED_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, next, *member);\ + return(*member!=NULL);\ + }\ + int sglib_##type##_len(type *list) {\ + int res;\ + SGLIB_SORTED_LIST_LEN(type, list, next, res);\ + return(res);\ + }\ + void sglib_##type##_sort(type **list) { \ + SGLIB_LIST_SORT(type, *list, comparator, next);\ + }\ + \ + type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ + it->subcomparator = subcomparator;\ + it->equalto = equalto;\ + it->nextelem = list;\ + return(sglib_##type##_it_next(it));\ + }\ + type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ + return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ + }\ + type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ + return(it->currentelem);\ + }\ + type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ + type *ce, *eq;\ + int (*scp)(type *, type *);\ + int c;\ + ce = it->nextelem;\ + it->nextelem = NULL;\ + if (it->subcomparator != NULL) {\ + eq = it->equalto; \ + scp = it->subcomparator;\ + while (ce!=NULL && (c=scp(ce, eq)) < 0) ce = ce->next;\ + if (ce != NULL && c > 0) ce = NULL;\ + }\ + it->currentelem = ce;\ + if (ce != NULL) it->nextelem = ce->next;\ + return(ce);\ + } + + +/* ----------------------------- double linked list (level 1) ------------------------------ */ + + +#define SGLIB_DEFINE_DL_LIST_PROTOTYPES(type, comparator, previous, next) \ + struct sglib_##type##_iterator {\ + type *currentelem;\ + type *prevelem;\ + type *nextelem;\ + int (*subcomparator)(type *, type *);\ + type *equalto;\ + };\ + extern void sglib_##type##_add(type **list, type *elem);\ + extern void sglib_##type##_add_before(type **list, type *elem);\ + extern void sglib_##type##_add_after(type **list, type *elem);\ + extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ + extern int sglib_##type##_add_before_if_not_member(type **list, type *elem, type **member);\ + extern int sglib_##type##_add_after_if_not_member(type **list, type *elem, type **member);\ + extern void sglib_##type##_concat(type **first, type *second);\ + extern void sglib_##type##_delete(type **list, type *elem);\ + extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ + extern int sglib_##type##_is_member(type *list, type *elem);\ + extern type *sglib_##type##_find_member(type *list, type *elem);\ + extern type *sglib_##type##_get_first(type *list);\ + extern type *sglib_##type##_get_last(type *list);\ + extern void sglib_##type##_sort(type **list);\ + extern int sglib_##type##_len(type *list);\ + extern void sglib_##type##_reverse(type **list);\ + extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ + extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ + extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); + + +#define SGLIB_DEFINE_DL_LIST_FUNCTIONS(type, comparator, previous, next) \ + void sglib_##type##_add(type **list, type *elem) {\ + SGLIB_DL_LIST_ADD(type, *list, elem, previous, next);\ + }\ + void sglib_##type##_add_after(type **list, type *elem) {\ + SGLIB_DL_LIST_ADD_AFTER(type, *list, elem, previous, next);\ + }\ + void sglib_##type##_add_before(type **list, type *elem) {\ + SGLIB_DL_LIST_ADD_BEFORE(type, *list, elem, previous, next);\ + }\ + int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ + SGLIB_DL_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ + return(*member==NULL);\ + }\ + int sglib_##type##_add_after_if_not_member(type **list, type *elem, type **member) {\ + SGLIB_DL_LIST_ADD_AFTER_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ + return(*member==NULL);\ + }\ + int sglib_##type##_add_before_if_not_member(type **list, type *elem, type **member) {\ + SGLIB_DL_LIST_ADD_BEFORE_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ + return(*member==NULL);\ + }\ + void sglib_##type##_concat(type **first, type *second) {\ + SGLIB_DL_LIST_CONCAT(type, *first, second, previous, next);\ + }\ + void sglib_##type##_delete(type **list, type *elem) {\ + SGLIB_DL_LIST_DELETE(type, *list, elem, previous, next);\ + }\ + int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ + SGLIB_DL_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, previous, next, *member);\ + return(*member!=NULL);\ + }\ + int sglib_##type##_is_member(type *list, type *elem) {\ + int result;\ + SGLIB_DL_LIST_IS_MEMBER(type, list, elem, previous, next, result);\ + return(result);\ + }\ + type *sglib_##type##_find_member(type *list, type *elem) {\ + type *result;\ + SGLIB_DL_LIST_FIND_MEMBER(type, list, elem, comparator, previous, next, result);\ + return(result);\ + }\ + type *sglib_##type##_get_first(type *list) {\ + type *result;\ + SGLIB_DL_LIST_GET_FIRST(type, list, previous, next, result);\ + return(result);\ + }\ + type *sglib_##type##_get_last(type *list) {\ + type *result;\ + SGLIB_DL_LIST_GET_LAST(type, list, previous, next, result);\ + return(result);\ + }\ + void sglib_##type##_sort(type **list) {\ + SGLIB_DL_LIST_SORT(type, *list, comparator, previous, next);\ + }\ + int sglib_##type##_len(type *list) {\ + int res;\ + SGLIB_DL_LIST_LEN(type, list, previous, next, res);\ + return(res);\ + }\ + void sglib_##type##_reverse(type **list) {\ + SGLIB_DL_LIST_REVERSE(type, *list, previous, next);\ + }\ + \ + type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ + it->subcomparator = subcomparator;\ + it->equalto = equalto;\ + it->prevelem = list;\ + it->nextelem = list;\ + if (list != NULL) it->nextelem = list->next;\ + return(sglib_##type##_it_next(it));\ + }\ + type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ + return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ + }\ + type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ + return(it->currentelem);\ + }\ + type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ + type *ce, *eq;\ + int (*scp)(type *, type *);\ + ce = it->prevelem;\ + it->prevelem = NULL;\ + if (it->subcomparator != NULL) {\ + eq = it->equalto; \ + scp = it->subcomparator;\ + while (ce!=NULL && scp(eq, ce)!=0) ce = ce->previous;\ + }\ + if (ce != NULL) {\ + it->prevelem = ce->previous;\ + } else {\ + ce = it->nextelem;\ + it->nextelem = NULL;\ + if (it->subcomparator != NULL) {\ + eq = it->equalto; \ + scp = it->subcomparator;\ + while (ce!=NULL && scp(ce, eq)!=0) ce = ce->next;\ + }\ + if (ce != NULL) it->nextelem = ce->next;\ + }\ + it->currentelem = ce;\ + return(ce);\ + } + + +/* --------------------------------- red-black trees (level 1) -------------------------------- */ + +/* + +This implementation requires pointers to left and right sons (no +parent pointer is needed) and one bit of additional information +storing the color of the node. The implementation follows discrepancy +fixing rules from: +http://www.cis.ohio-state.edu/~gurari/course/cis680/cis680Ch11.html + +*/ + +#define SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, leftt, rightt, bits, RED, BLACK) {\ + type *t, *tl, *a, *b, *c, *ar, *bl, *br, *cl, *cr;\ + t = *tree;\ + tl = t->leftt;\ + if (t->rightt!=NULL && SGLIB___GET_VALUE(t->rightt->bits)==RED) {\ + if (SGLIB___GET_VALUE(tl->bits)==RED) {\ + if ((tl->leftt!=NULL && SGLIB___GET_VALUE(tl->leftt->bits)==RED) \ + || (tl->rightt!=NULL && SGLIB___GET_VALUE(tl->rightt->bits)==RED)) {\ + SGLIB___SET_VALUE(t->leftt->bits,BLACK);\ + SGLIB___SET_VALUE(t->rightt->bits,BLACK);\ + SGLIB___SET_VALUE(t->bits,RED);\ + }\ + }\ + } else {\ + if (SGLIB___GET_VALUE(tl->bits)==RED) {\ + if (tl->leftt!=NULL && SGLIB___GET_VALUE(tl->leftt->bits)==RED) {\ + a = t; b = tl; c = tl->leftt;\ + br = b->rightt;\ + a->leftt = br;\ + b->leftt = c; b->rightt = a;\ + SGLIB___SET_VALUE(a->bits,RED);\ + SGLIB___SET_VALUE(b->bits,BLACK);\ + *tree = b;\ + } else if (tl->rightt!=NULL && SGLIB___GET_VALUE(tl->rightt->bits)==RED) {\ + a = t; b = tl; ar=a->rightt;\ + bl=b->leftt; c=b->rightt;\ + cl=c->leftt; cr=c->rightt;\ + b->rightt = cl;\ + a->leftt = cr;\ + c->leftt = b;\ + c->rightt = a;\ + SGLIB___SET_VALUE(c->bits,BLACK);\ + SGLIB___SET_VALUE(a->bits,RED);\ + *tree = c;\ + }\ + }\ + }\ +} + +#define SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, leftt, rightt, bits, RED, BLACK, res) {\ + type *t, *a, *b, *c, *d, *ar, *bl, *br, *cl, *cr, *dl, *dr;\ + t = a = *tree;\ + assert(t!=NULL);\ + ar = a->rightt;\ + b = t->leftt;\ + if (b==NULL) {\ + assert(SGLIB___GET_VALUE(t->bits)==RED);\ + SGLIB___SET_VALUE(t->bits,BLACK);\ + res = 0;\ + } else {\ + bl = b->leftt;\ + br = b->rightt;\ + if (SGLIB___GET_VALUE(b->bits)==RED) {\ + if (br==NULL) {\ + *tree = b;\ + SGLIB___SET_VALUE(b->bits,BLACK);\ + b->rightt = a;\ + a->leftt = br;\ + res = 0;\ + } else {\ + c = br;\ + assert(c!=NULL && SGLIB___GET_VALUE(c->bits)==BLACK);\ + cl = c->leftt;\ + cr = c->rightt;\ + if ((cl==NULL||SGLIB___GET_VALUE(cl->bits)==BLACK) && (cr==NULL||SGLIB___GET_VALUE(cr->bits)==BLACK)) {\ + *tree = b;\ + b->rightt = a;\ + SGLIB___SET_VALUE(b->bits,BLACK);\ + a->leftt = c;\ + SGLIB___SET_VALUE(c->bits,RED);\ + res = 0;\ + } else if (cl!=NULL && SGLIB___GET_VALUE(cl->bits)==RED) {\ + if (cr!=NULL && SGLIB___GET_VALUE(cr->bits)==RED) {\ + d = cr;\ + dl = d->leftt;\ + dr = d->rightt;\ + *tree = d;\ + SGLIB___SET_VALUE(d->bits,BLACK);\ + d->leftt = b;\ + c->rightt = dl;\ + d->rightt = a;\ + a->leftt = dr;\ + res = 0;\ + } else {\ + *tree = c;\ + c->leftt = b;\ + c->rightt = a;\ + b->leftt = bl;\ + b->rightt = cl;\ + a->leftt = cr;\ + SGLIB___SET_VALUE(cl->bits,BLACK);\ + res = 0;\ + }\ + } else if (cr!=NULL && SGLIB___GET_VALUE(cr->bits)==RED) {\ + assert(cl==NULL || SGLIB___GET_VALUE(cl->bits)==BLACK);\ + d = cr;\ + dl = d->leftt;\ + dr = d->rightt;\ + *tree = d;\ + SGLIB___SET_VALUE(d->bits,BLACK);\ + d->leftt = b;\ + c->rightt = dl;\ + d->rightt = a;\ + a->leftt = dr;\ + res = 0;\ + } else {\ + assert(0);\ + res = 0;\ + }\ + }\ + } else {\ + if ((bl==NULL || SGLIB___GET_VALUE(bl->bits)==BLACK) && (br==NULL || SGLIB___GET_VALUE(br->bits)==BLACK)) {\ + res = (SGLIB___GET_VALUE(a->bits)==BLACK);\ + SGLIB___SET_VALUE(a->bits,BLACK);\ + SGLIB___SET_VALUE(b->bits,RED);\ + } else if (bl!=NULL && SGLIB___GET_VALUE(bl->bits)==RED) {\ + if (br==NULL || SGLIB___GET_VALUE(br->bits)==BLACK) {\ + *tree = b;\ + SGLIB___SET_VALUE(b->bits,SGLIB___GET_VALUE(a->bits));\ + SGLIB___SET_VALUE(a->bits,BLACK);\ + b->rightt = a;\ + a->leftt = br;\ + SGLIB___SET_VALUE(bl->bits,BLACK);\ + res = 0;\ + } else {\ + assert(bl!=NULL);\ + assert(br!=NULL);\ + assert(SGLIB___GET_VALUE(bl->bits)==RED);\ + assert(SGLIB___GET_VALUE(br->bits)==RED);\ + c = br;\ + cl = c->leftt;\ + cr = c->rightt;\ + *tree = c;\ + SGLIB___SET_VALUE(c->bits,SGLIB___GET_VALUE(a->bits));\ + SGLIB___SET_VALUE(a->bits,BLACK);\ + c->leftt = b;\ + c->rightt = a;\ + b->rightt = cl;\ + a->leftt = cr;\ + res = 0;\ + }\ + } else {\ + assert(br!=NULL && SGLIB___GET_VALUE(br->bits)==RED);\ + c = br;\ + cl = c->leftt;\ + cr = c->rightt;\ + *tree = c;\ + SGLIB___SET_VALUE(c->bits,SGLIB___GET_VALUE(a->bits));\ + SGLIB___SET_VALUE(a->bits,BLACK);\ + c->leftt = b;\ + c->rightt = a;\ + b->rightt = cl;\ + a->leftt = cr;\ + res = 0;\ + }\ + }\ + }\ +} + + +#define SGLIB_DEFINE_RBTREE_FUNCTIONS_GENERAL(type, left, right, bits, comparator, RED, BLACK) \ +static void sglib___##type##_fix_left_insertion_discrepancy(type **tree) {\ + SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, left, right, bits, RED, BLACK);\ +}\ +\ +static void sglib___##type##_fix_right_insertion_discrepancy(type **tree) {\ + SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, right, left, bits, RED, BLACK);\ +}\ +\ +static int sglib___##type##_fix_left_deletion_discrepancy(type **tree) {\ + int res;\ + SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, right, left, bits, RED, BLACK, res);\ + return(res);\ +}\ +\ +static int sglib___##type##_fix_right_deletion_discrepancy(type **tree) {\ + int res;\ + SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, left, right, bits, RED, BLACK, res);\ + return(res);\ +}\ +\ +static void sglib___##type##_add_recursive(type **tree, type *elem) {\ + int cmp;\ + type *t;\ + t = *tree;\ + if (t == NULL) {\ + SGLIB___SET_VALUE(elem->bits,RED);\ + *tree =elem;\ + } else {\ + cmp = comparator(elem, t);\ + if (cmp < 0 || (cmp==0 && elemleft, elem);\ + if (SGLIB___GET_VALUE(t->bits)==BLACK) sglib___##type##_fix_left_insertion_discrepancy(tree);\ + } else {\ + sglib___##type##_add_recursive(&t->right, elem);\ + if (SGLIB___GET_VALUE(t->bits)==BLACK) sglib___##type##_fix_right_insertion_discrepancy(tree);\ + }\ + }\ +}\ +\ +static int sglib___##type##_delete_rightmost_leaf(type **tree, type **theLeaf) {\ + type *t;\ + int res, deepDecreased;\ + t = *tree;\ + res = 0;\ + assert(t!=NULL);\ + if (t->right == NULL) {\ + *theLeaf = t;\ + if (t->left!=NULL) {\ + if (SGLIB___GET_VALUE(t->bits)==BLACK && SGLIB___GET_VALUE(t->left->bits)==BLACK) res = 1;\ + SGLIB___SET_VALUE(t->left->bits,BLACK);\ + *tree = t->left;\ + } else {\ + *tree = NULL;\ + res = (SGLIB___GET_VALUE(t->bits)==BLACK);\ + }\ + } else {\ + deepDecreased = sglib___##type##_delete_rightmost_leaf(&t->right, theLeaf);\ + if (deepDecreased) res = sglib___##type##_fix_right_deletion_discrepancy(tree);\ + }\ + return(res);\ +}\ +\ +int sglib___##type##_delete_recursive(type **tree, type *elem) {\ + type *t, *theLeaf;\ + int cmp, res, deepDecreased;\ + t = *tree;\ + res = 0;\ + if (t==NULL) {\ + assert(0 && "The element to delete not found in the tree, use 'delete_if_member'"!=NULL);\ + } else {\ + cmp = comparator(elem, t);\ + if (cmp < 0 || (cmp==0 && elemleft, elem);\ + if (deepDecreased) {\ + res = sglib___##type##_fix_left_deletion_discrepancy(tree);\ + }\ + } else if (cmp > 0 || (cmp==0 && elem>t)) {\ + deepDecreased = sglib___##type##_delete_recursive(&t->right, elem);\ + if (deepDecreased) {\ + res = sglib___##type##_fix_right_deletion_discrepancy(tree);\ + }\ + } else {\ + assert(elem==t && "Deleting an element which is non member of the tree, use 'delete_if_member'"!=NULL);\ + if (t->left == NULL) {\ + if (t->right == NULL) {\ + /* a leaf, delete, it; */\ + *tree = NULL;\ + res = (SGLIB___GET_VALUE(t->bits)==BLACK);\ + } else {\ + if (SGLIB___GET_VALUE(t->bits)==0 && SGLIB___GET_VALUE(t->right->bits)==0) res = 1;\ + SGLIB___SET_VALUE(t->right->bits,BLACK);\ + *tree = t->right;\ + }\ + } else {\ + /* propagate deletion until righmost leaf of left subtree */\ + deepDecreased = sglib___##type##_delete_rightmost_leaf(&t->left, &theLeaf);\ + theLeaf->left = t->left;\ + theLeaf->right = t->right;\ + SGLIB___SET_VALUE(theLeaf->bits,SGLIB___GET_VALUE(t->bits));\ + *tree = theLeaf;\ + if (deepDecreased) res = sglib___##type##_fix_left_deletion_discrepancy(tree);\ + }\ + }\ + }\ + return(res);\ +}\ +\ +void sglib_##type##_add(type **tree, type *elem) {\ + elem->left = elem->right = NULL;\ + sglib___##type##_add_recursive(tree, elem);\ + SGLIB___SET_VALUE((*tree)->bits,BLACK);\ +}\ +\ +void sglib_##type##_delete(type **tree, type *elem) {\ + sglib___##type##_delete_recursive(tree, elem);\ + if (*tree!=NULL) SGLIB___SET_VALUE((*tree)->bits,BLACK);\ +}\ +\ +type *sglib_##type##_find_member(type *t, type *elem) {\ + type *res;\ + SGLIB___BIN_TREE_FIND_MEMBER(type, t, elem, left, right, comparator, res);\ + return(res);\ +}\ +\ +int sglib_##type##_is_member(type *t, type *elem) {\ + int cmp;\ + while (t!=NULL) {\ + cmp = comparator(elem, t);\ + if (cmp < 0 || (cmp==0 && elemleft;\ + } else if (cmp > 0 || (cmp==0 && elem>t)) {\ + t = t->right;\ + } else {\ + assert(t == elem);\ + return(1);\ + }\ + }\ + return(0);\ +}\ +\ +int sglib_##type##_delete_if_member(type **tree, type *elem, type **memb) {\ + if ((*memb=sglib_##type##_find_member(*tree, elem))!=NULL) {\ + sglib_##type##_delete(tree, *memb);\ + return(1);\ + } else {\ + return(0);\ + }\ +}\ +int sglib_##type##_add_if_not_member(type **tree, type *elem, type **memb) {\ + if ((*memb=sglib_##type##_find_member(*tree, elem))==NULL) {\ + sglib_##type##_add(tree, elem);\ + return(1);\ + } else {\ + return(0);\ + }\ +}\ +int sglib_##type##_len(type *t) {\ + int n;\ + type *e;\ + n = 0;\ + SGLIB_BIN_TREE_MAP_ON_ELEMENTS(type, t, e, left, right, n++);\ + return(n);\ +}\ +\ +void sglib__##type##_it_compute_current_elem(struct sglib_##type##_iterator *it) {\ + int i,j,cmp;\ + type *s, *eqt;\ + int (*subcomparator)(type *, type *);\ + eqt = it->equalto;\ + subcomparator = it->subcomparator;\ + it->currentelem = NULL;\ + while(it->pathi > 0 && it->currentelem==NULL) {\ + i = it->pathi-1;\ + if (i >= 0) {\ + if (it->pass[i] >= 2) {\ + /* goto up */\ + it->pathi --;\ + } else {\ + if (it->pass[i] == 0) {\ + /* goto left */\ + s = it->path[i]->left;\ + } else {\ + /* goto right */\ + s = it->path[i]->right;\ + }\ + if (eqt != NULL) {\ + if (subcomparator == NULL) {\ + SGLIB___BIN_TREE_FIND_MEMBER(type, s, eqt, left, right, comparator, s);\ + } else {\ + SGLIB___BIN_TREE_FIND_MEMBER(type, s, eqt, left, right, subcomparator, s);\ + }\ + }\ + if (s != NULL) {\ + j = i+1;\ + it->path[j] = s;\ + it->pass[j] = 0;\ + it->pathi ++;\ + }\ + it->pass[i] ++;\ + }\ + }\ + if (it->pathi>0 && it->order == it->pass[it->pathi-1]) {\ + it->currentelem = it->path[it->pathi-1];\ + }\ + }\ +}\ +type *sglib__##type##_it_init(struct sglib_##type##_iterator *it, type *tree, int order, int (*subcomparator)(type *, type *), type *equalto) {\ + type *t;\ + assert(it!=NULL);\ + it->order = order;\ + it->equalto = equalto;\ + it->subcomparator = subcomparator;\ + if (equalto == NULL) { \ + t = tree;\ + } else {\ + if (subcomparator == NULL) {\ + SGLIB___BIN_TREE_FIND_MEMBER(type, tree, equalto, left, right, comparator, t);\ + } else {\ + SGLIB___BIN_TREE_FIND_MEMBER(type, tree, equalto, left, right, subcomparator, t);\ + }\ + }\ + if (t == NULL) {\ + it->pathi = 0;\ + it->currentelem = NULL;\ + } else {\ + it->pathi = 1;\ + it->pass[0] = 0;\ + it->path[0] = t;\ + if (order == 0) {\ + it->currentelem = t;\ + } else {\ + sglib__##type##_it_compute_current_elem(it);\ + }\ + }\ + return(it->currentelem);\ +}\ +type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *tree) {\ + return(sglib__##type##_it_init(it, tree, 2, NULL, NULL));\ +}\ +type *sglib_##type##_it_init_preorder(struct sglib_##type##_iterator *it, type *tree) {\ + return(sglib__##type##_it_init(it, tree, 0, NULL, NULL));\ +}\ +type *sglib_##type##_it_init_inorder(struct sglib_##type##_iterator *it, type *tree) {\ + return(sglib__##type##_it_init(it, tree, 1, NULL, NULL));\ +}\ +type *sglib_##type##_it_init_postorder(struct sglib_##type##_iterator *it, type *tree) {\ + return(sglib__##type##_it_init(it, tree, 2, NULL, NULL));\ +}\ +type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *tree, int (*subcomparator)(type *, type *), type *equalto) {\ + return(sglib__##type##_it_init(it, tree, 1, subcomparator, equalto));\ +}\ +type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ + return(it->currentelem);\ +}\ +type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ + sglib__##type##_it_compute_current_elem(it);\ + return(it->currentelem);\ +}\ +\ +static void sglib___##type##_consistency_check_recursive(type *t, int *pathdeep, int cdeep) {\ + if (t==NULL) {\ + if (*pathdeep < 0) *pathdeep = cdeep;\ + else assert(*pathdeep == cdeep);\ + } else {\ + if (t->left!=NULL) assert(comparator(t->left, t) <= 0);\ + if (t->right!=NULL) assert(comparator(t, t->right) <= 0);\ + if (SGLIB___GET_VALUE(t->bits) == RED) {\ + assert(t->left == NULL || SGLIB___GET_VALUE(t->left->bits)==BLACK);\ + assert(t->right == NULL || SGLIB___GET_VALUE(t->right->bits)==BLACK);\ + sglib___##type##_consistency_check_recursive(t->left, pathdeep, cdeep);\ + sglib___##type##_consistency_check_recursive(t->right, pathdeep, cdeep);\ + } else {\ + sglib___##type##_consistency_check_recursive(t->left, pathdeep, cdeep+1);\ + sglib___##type##_consistency_check_recursive(t->right, pathdeep, cdeep+1);\ + }\ + }\ +}\ +\ +void sglib___##type##_consistency_check(type *t) {\ + int pathDeep;\ + assert(t==NULL || SGLIB___GET_VALUE(t->bits) == BLACK);\ + pathDeep = -1;\ + sglib___##type##_consistency_check_recursive(t, &pathDeep, 0);\ +} + + +#define SGLIB_DEFINE_RBTREE_PROTOTYPES(type, left, right, colorbit, comparator) \ + struct sglib_##type##_iterator {\ + type *currentelem;\ + char pass[SGLIB_MAX_TREE_DEEP];\ + type *path[SGLIB_MAX_TREE_DEEP];\ + short int pathi;\ + short int order;\ + type *equalto;\ + int (*subcomparator)(type *, type *);\ + };\ + extern void sglib___##type##_consistency_check(type *t); \ + extern void sglib_##type##_add(type **tree, type *elem); \ + extern int sglib_##type##_add_if_not_member(type **tree, type *elem, type **memb); \ + extern void sglib_##type##_delete(type **tree, type *elem); \ + extern int sglib_##type##_delete_if_member(type **tree, type *elem, type **memb); \ + extern int sglib_##type##_is_member(type *t, type *elem); \ + extern type *sglib_##type##_find_member(type *t, type *elem); \ + extern int sglib_##type##_len(type *t); \ + extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *tree); \ + extern type *sglib_##type##_it_init_preorder(struct sglib_##type##_iterator *it, type *tree); \ + extern type *sglib_##type##_it_init_inorder(struct sglib_##type##_iterator *it, type *tree); \ + extern type *sglib_##type##_it_init_postorder(struct sglib_##type##_iterator *it, type *tree); \ + extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *tree, int (*subcomparator)(type *, type *), type *equalto); \ + extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ + extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); \ + + +#define SGLIB_DEFINE_RBTREE_FUNCTIONS(type, left, right, colorbit, comparator) \ + SGLIB_DEFINE_RBTREE_FUNCTIONS_GENERAL(type, left, right, colorbit, comparator, 1, 0) + + + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ +/* - SUPPLEMENTARY DEFINITIONS - */ +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + + +#define SGLIB___GET_VALUE(x) (x) +#define SGLIB___SET_VALUE(x, value) {(x) = (value);} +#define SGLIB_ARRAY_ELEMENTS_EXCHANGER(type, a, i, j) {type _sgl_aee_tmp_; _sgl_aee_tmp_=(a)[(i)]; (a)[(i)]=(a)[(j)]; (a)[(j)]= _sgl_aee_tmp_;} + + +#define SGLIB_SAFE_NUMERIC_COMPARATOR(x, y) (((x)>(y)?1:((x)<(y)?-1:0))) +#define SGLIB_SAFE_REVERSE_NUMERIC_COMPARATOR(x, y) (((x)>(y)?-1:((x)<(y)?1:0))) +#define SGLIB_FAST_NUMERIC_COMPARATOR(x, y) ((int)((x) - (y))) +#define SGLIB_FAST_REVERSE_NUMERIC_COMPARATOR(x, y) ((int)((y) - (x))) +#define SGLIB_NUMERIC_COMPARATOR(x, y) SGLIB_SAFE_NUMERIC_COMPARATOR(x, y) +#define SGLIB_REVERSE_NUMERIC_COMPARATOR(x, y) SGLIB_SAFE_REVERSE_NUMERIC_COMPARATOR(x, y) + +#ifndef SGLIB_MAX_TREE_DEEP +#define SGLIB_MAX_TREE_DEEP 128 +#endif + +#ifndef SGLIB_HASH_TAB_SHIFT_CONSTANT +#define SGLIB_HASH_TAB_SHIFT_CONSTANT 16381 /* should be a prime */ +/* #define SGLIB_HASH_TAB_SHIFT_CONSTANT 536870912*/ /* for large tables :) */ +#endif + +#endif /* _SGLIB__h_ */ diff --git a/src/ucs/datastruct/sglib_wrapper.h b/src/ucs/datastruct/sglib_wrapper.h new file mode 100644 index 00000000000..4dfa9289e0f --- /dev/null +++ b/src/ucs/datastruct/sglib_wrapper.h @@ -0,0 +1,22 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_SGLIB_WRAPPER_H +#define UCS_SGLIB_WRAPPER_H + +#include "sglib.h" + +/* + * Fix "unused variable" + */ +#undef SGLIB_LIST_LEN +#define SGLIB_LIST_LEN(type, list, next, result) {\ + (result) = 0;\ + SGLIB_LIST_MAP_ON_ELEMENTS(type, list, _ce_, next, (result)++);\ +} + +#endif diff --git a/src/ucs/debug/check.h b/src/ucs/debug/check.h new file mode 100644 index 00000000000..9f949f68696 --- /dev/null +++ b/src/ucs/debug/check.h @@ -0,0 +1,22 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef _UCS_CHECK_H +#define _UCS_CHECK_H + +#include +#include + +#if ENABLE_ASSERT +# define UCS_REENTRY_GUARD(_enter_count) \ + ucs_assertv(_enter_count == 0, "%s called recursively", __FUNCTION__); \ + for (++(_enter_count); (_enter_count) > 0; --(_enter_count)) +#else +# define UCS_REENTRY_GUARD(_enter_count) +#endif + +#endif diff --git a/src/ucs/debug/debug.c b/src/ucs/debug/debug.c new file mode 100644 index 00000000000..3d55cca5713 --- /dev/null +++ b/src/ucs/debug/debug.c @@ -0,0 +1,731 @@ +/** + * Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. + * + * $COPYRIGHT$ + * $HEADER$ + */ + +#define _GNU_SOURCE + +#include "debug.h" +#include "log.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_DETAILED_BACKTRACE +# if HAVE_LIBIBERTY_H +# include +#elif HAVE_LIBIBERTY_LIBIBERTY_H +# include +# endif +# include +#endif /* HAVE_DETAILED_BACKTRACE */ + + +#define UCS_GDB_MAX_ARGS 32 +#define BACKTRACE_MAX 64 + +struct dl_address_search { + unsigned long address; + const char *filename; + unsigned long base; +}; + +#ifdef HAVE_DETAILED_BACKTRACE + +struct backtrace_line { + unsigned long address; + char *file; + char *function; + unsigned lineno; +}; + +struct backtrace_file { + struct dl_address_search dl; + bfd *abfd; + asymbol **syms; +}; + +typedef struct backtrace *backtrace_h; +struct backtrace { + struct backtrace_line lines[BACKTRACE_MAX]; + int size; + int position; +}; + +struct backtrace_search { + int count; + struct backtrace_file *file; + int backoff; /* search the line where the function call + took place, instead of return address */ + struct backtrace_line *lines; + int max_lines; +}; + +#endif /* HAVE_DETAILED_BACKTRACE */ + +#define UCS_SYS_SIGNAME(signame) [SIG ## signame] = #signame +const char *ucs_signal_names[] = { + [0] = "SIGNAL0", + UCS_SYS_SIGNAME(HUP), + UCS_SYS_SIGNAME(INT), + UCS_SYS_SIGNAME(QUIT), + UCS_SYS_SIGNAME(ILL), + UCS_SYS_SIGNAME(TRAP), + UCS_SYS_SIGNAME(ABRT), + UCS_SYS_SIGNAME(BUS), + UCS_SYS_SIGNAME(FPE), + UCS_SYS_SIGNAME(KILL), + UCS_SYS_SIGNAME(USR1), + UCS_SYS_SIGNAME(SEGV), + UCS_SYS_SIGNAME(USR2), + UCS_SYS_SIGNAME(PIPE), + UCS_SYS_SIGNAME(ALRM), + UCS_SYS_SIGNAME(TERM), + UCS_SYS_SIGNAME(STKFLT), + UCS_SYS_SIGNAME(CHLD), + UCS_SYS_SIGNAME(CONT), + UCS_SYS_SIGNAME(STOP), + UCS_SYS_SIGNAME(TSTP), + UCS_SYS_SIGNAME(TTIN), + UCS_SYS_SIGNAME(TTOU), + UCS_SYS_SIGNAME(URG), + UCS_SYS_SIGNAME(XCPU), + UCS_SYS_SIGNAME(XFSZ), + UCS_SYS_SIGNAME(VTALRM), + UCS_SYS_SIGNAME(PROF), + UCS_SYS_SIGNAME(WINCH), + UCS_SYS_SIGNAME(IO), + UCS_SYS_SIGNAME(PWR), + UCS_SYS_SIGNAME(SYS), + [SIGSYS + 1] = NULL +}; + + +static int dl_match_address(struct dl_phdr_info *info, size_t size, void *data) +{ + struct dl_address_search *dl = data; + const ElfW(Phdr) *phdr; + ElfW(Addr) load_base = info->dlpi_addr; + long n; + + phdr = info->dlpi_phdr; + for (n = info->dlpi_phnum; --n >= 0; phdr++) { + if (phdr->p_type == PT_LOAD) { + ElfW(Addr) vbaseaddr = phdr->p_vaddr + load_base; + if (dl->address >= vbaseaddr && dl->address < vbaseaddr + phdr->p_memsz) { + dl->filename = info->dlpi_name; + dl->base = info->dlpi_addr; + } + } + } + return 0; +} + +static int dl_lookup_address(struct dl_address_search *dl) +{ + dl->filename = NULL; + dl->base = 0; + + dl_iterate_phdr(dl_match_address, dl); + if (dl->filename == NULL) { + return 0; + } + + if (strlen(dl->filename) == 0) { + dl->filename = ucs_get_exe(); + } + return 1; +} + +#ifdef HAVE_DETAILED_BACKTRACE + +/* + * The dl member in file should be initialized + */ +static int load_file(struct backtrace_file *file) +{ + long symcount; + unsigned int size; + char **matching; + + file->syms = NULL; + file->abfd = bfd_openr(file->dl.filename, NULL); + if (!file->abfd) { + goto err; + } + + if (bfd_check_format(file->abfd, bfd_archive)) { + goto err_close; + } + + if (!bfd_check_format_matches(file->abfd, bfd_object, &matching)) { + goto err_close; + } + + if ((bfd_get_file_flags(file->abfd) & HAS_SYMS) == 0) { + goto err_close; + } + + symcount = bfd_read_minisymbols(file->abfd, 0, (PTR)&file->syms, &size); + if (symcount == 0) { + free(file->syms); + symcount = bfd_read_minisymbols(file->abfd, 1, (PTR)&file->syms, &size); + } + if (symcount < 0) { + goto err_close; + } + + return 1; + +err_close: + bfd_close(file->abfd); +err: + return 0; +} + +static void unload_file(struct backtrace_file *file) +{ + free(file->syms); + bfd_close(file->abfd); +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ + struct backtrace_search *search = data; + bfd_size_type size; + bfd_vma vma; + unsigned long address; + const char *filename, *function; + unsigned lineno; + int found; + + if ((search->count > 0) || (search->max_lines == 0) || + ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)) { + return; + } + + address = search->file->dl.address - search->file->dl.base; + vma = bfd_get_section_vma(abfd, section); + if (address < vma) { + return; + } + + size = bfd_section_size(abfd, section); + if (address >= vma + size) { + return; + } + + /* Search in address-1 to get the calling line instead of return address */ + found = bfd_find_nearest_line(abfd, section, search->file->syms, + address - vma - search->backoff, + &filename, &function, &lineno); + do { + search->lines[search->count].address = address; + search->lines[search->count].file = filename ? strdup(filename) : NULL; + search->lines[search->count].function = function ? strdup(function) : NULL; + search->lines[search->count].lineno = lineno; + if (search->count == 0) { + /* To get the inliner info, search at the original address */ + bfd_find_nearest_line(abfd, section, search->file->syms, address - vma, + &filename, &function, &lineno); + } + + ++search->count; + found = bfd_find_inliner_info(abfd, &filename, &function, &lineno); + } while (found && (search->count < search->max_lines)); +} + +static int get_line_info(struct backtrace_file *file, int backoff, + struct backtrace_line *lines, int max) +{ + struct backtrace_search search; + + search.file = file; + search.backoff = backoff; + search.count = 0; + search.lines = lines; + search.max_lines = max; + bfd_map_over_sections(file->abfd, find_address_in_section, &search); + return search.count; +} + +/** + * Create a backtrace from the calling location. + * + * @return Backtrace object. + */ +backtrace_h backtrace_create(void) +{ + struct backtrace_file file; + void *addresses[BACKTRACE_MAX]; + int i, num_addresses; + backtrace_h bckt; + + bckt = malloc(sizeof *bckt); + if (!bckt) { + return NULL; + } + + num_addresses = backtrace(addresses, BACKTRACE_MAX); + + bckt->size = 0; + for (i = 0; i < num_addresses; ++i) { + file.dl.address = (unsigned long)addresses[i]; + if (dl_lookup_address(&file.dl) && load_file(&file)) { + bckt->size += get_line_info(&file, 1, bckt->lines + bckt->size, + BACKTRACE_MAX - bckt->size); + unload_file(&file); + } + } + + bckt->position = 0; + return bckt; +} + +/** + * Destroy a backtrace and free all memory. + * + * @param bckt Backtrace object. + */ +void backtrace_destroy(backtrace_h bckt) +{ + int i; + + for (i = 0; i < bckt->size; ++i) { + free(bckt->lines[i].function); + free(bckt->lines[i].file); + } + free(bckt); +} + +void ucs_debug_get_line_info(const char *filename, unsigned long base, + unsigned long address, ucs_debug_address_info_t *info) +{ + struct backtrace_file file; + struct backtrace_line line; + int count; + + file.dl.filename = filename; + file.dl.base = base; + file.dl.address = address; + + if (!load_file(&file)) { + goto err; + } + + count = get_line_info(&file, 0, &line, 1); + if (count == 0) { + goto err_unload; + } + + if (line.function) { + strncpy(info->function, line.function, sizeof(info->function)); + } else { + strcpy(info->function, "???"); + } + if (line.file) { + strncpy(info->source_file, line.file, sizeof(info->source_file)); + } else { + strcpy(info->function, "???"); + } + info->line_number = line.lineno; + + free(line.function); + free(line.file); + unload_file(&file); + return; + +err_unload: + unload_file(&file); +err: + strcpy(info->function, ""); + strcpy(info->source_file, ""); + info->line_number = 0; +} + +/** + * Walk to the next backtrace line information. + * + * @param bckt Backtrace object. + * @param address Filled with backtrace address. + * @param file Filled with a pointer to the source file name. + * @param function Filled with a pointer to function name. + * @param lineno Filled with source line number. + * + * NOTE: the file and function memory remains valid as long as the backtrace + * object is not destroyed. + */ +int backtrace_next(backtrace_h bckt, unsigned long *address, char const ** file, + char const ** function, unsigned *lineno) +{ + struct backtrace_line *line; + + if (bckt->position >= bckt->size) + return 0; + + line = &bckt->lines[bckt->position++]; + *address = line->address; + *file = line->file; + *function = line->function; + *lineno = line->lineno; + return 1; +} + +void ucs_debug_print_backtrace(FILE *stream, int strip) +{ + backtrace_h bckt; + unsigned long address; + const char *file, *function; + unsigned line; + int i; + + bckt = backtrace_create(); + + fprintf(stream, "==== backtrace ====\n"); + i = 0; + while (backtrace_next(bckt, &address, &file, &function, &line)) { + if (i >= strip) { + fprintf(stream, "%2d 0x%016lx %s() %s:%u\n", i, address, + function ? function : "??", file ? file : "??", line); + } + ++i; + } + fprintf(stream, "===================\n"); + + backtrace_destroy(bckt); +} + +const char *ucs_debug_get_symbol_name(void *address, char *buffer, size_t max) +{ + ucs_debug_address_info_t info; + ucs_debug_lookup_address(address, &info); + return strncpy(buffer, info.function, max); +} + +#else /* HAVE_DETAILED_BACKTRACE */ + +void ucs_debug_get_line_info(const char *filename, unsigned long base, unsigned long address, + ucs_debug_address_info_t *info) +{ + strcpy(info->function, ""); + strcpy(info->source_file, ""); + info->line_number = 0; +} + +void ucs_debug_print_backtrace(FILE *stream, int strip) +{ + char **symbols; + void *addresses[BACKTRACE_MAX]; + int count, i; + + fprintf(stream, "==== backtrace ====\n"); + + count = backtrace(addresses, BACKTRACE_MAX); + symbols = backtrace_symbols(addresses, count); + for (i = strip; i < count; ++i) { + fprintf(stream, " %2d %s\n", i - strip, symbols[i]); + } + free(symbols); + + fprintf(stream, "===================\n"); +} + +const char *ucs_debug_get_symbol_name(void *address, char *buffer, size_t max) +{ + Dl_info info; + int ret; + + ret = dladdr(address, &info); + if (ret != 0) { + return NULL; + } + + return strncpy(buffer, info.dli_sname, max); +} + +#endif /* HAVE_DETAILED_BACKTRACE */ + + +static ucs_status_t ucs_debugger_attach() +{ + static const char *gdb_commands = "bt\n"; + const char *cmds; + char *gdb_cmdline; + char gdb_commands_file[256]; + char* argv[6 + UCS_GDB_MAX_ARGS]; + pid_t pid, debug_pid; + int fd, ret, narg; + int valgrind; + char *self_exe; + + debug_pid = getpid(); + + /* Fork a process which will execute gdb */ + pid = fork(); + if (pid < 0) { + ucs_log_fatal_error("fork returned %d: %m", pid); + return UCS_ERR_IO_ERROR; + } + + valgrind = RUNNING_ON_VALGRIND; + self_exe = strdup(ucs_get_exe()); + + if (pid == 0) { + gdb_cmdline = strdup(ucs_global_opts.gdb_command); + narg = 0; + argv[narg] = strtok(gdb_cmdline, " \t"); + while (argv[narg] != NULL) { + ++narg; + argv[narg] = strtok(NULL, " \t"); + } + + if (!valgrind) { + argv[narg++] = "-p"; + if (asprintf(&argv[narg++], "%d", debug_pid)<0) { + ucs_log_fatal_error("Failed to extract pid : %m"); + exit(-1); + } + } + + /* Generate a file name for gdb commands */ + memset(gdb_commands_file, 0, sizeof(gdb_commands_file)); + snprintf(gdb_commands_file, sizeof(gdb_commands_file) - 1, + "/tmp/.gdbcommands.%s", getlogin()); + + /* Write gdb commands and add the file to argv is successful */ + fd = open(gdb_commands_file, O_WRONLY|O_TRUNC|O_CREAT, 0600); + if (fd >= 0) { + if (valgrind) { + if (asprintf((char**)&cmds, "file %s\n" + "target remote | vgdb\n" + "%s", + self_exe, gdb_commands) < 0) { + cmds = ""; + } + } else { + cmds = gdb_commands; + } + + if (write(fd, cmds, strlen(cmds)) == strlen(cmds)) { + argv[narg++] = "-x"; + argv[narg++] = gdb_commands_file; + } else { + ucs_log_fatal_error("Unable to write to command file: %m"); + } + close(fd); + } else { + ucs_log_fatal_error("Unable to open '%s' for writing: %m", + gdb_commands_file); + } + + argv[narg++] = NULL; + + /* Execute GDB */ + ret = execvp(argv[0], argv); + if (ret < 0) { + ucs_log_fatal_error("Failed to execute %s: %m", argv[0]); + exit(-1); + } + } + + free(self_exe); + waitpid(pid, &ret, 0); + return UCS_OK; +} + +static void UCS_F_NOINLINE ucs_debug_freeze() +{ + static volatile int freeze = 1; + while (freeze) { + pause(); + } +} + +static int ucs_debug_stop_exclude_thread = -1; +static void ucs_debug_stop_handler(int signo) +{ + if (ucs_get_tid() == ucs_debug_stop_exclude_thread) { + return; + } + + ucs_debug_freeze(); +} + +static void ucs_debug_stop_other_threads() +{ + ucs_debug_stop_exclude_thread = ucs_get_tid(); + signal(SIGUSR1, ucs_debug_stop_handler); + kill(0, SIGUSR1); +} + +static ucs_status_t ucs_error_freeze() +{ + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + char response; + int ret; + + ucs_debug_stop_other_threads(); + + if (pthread_mutex_trylock(&lock) == 0) { + + if (strlen(ucs_global_opts.gdb_command) && isatty(fileno(stdout) && + isatty(fileno(stdin)))) + { + ucs_log_fatal_error("Process frozen, press Enter to attach a debugger..."); + ret = read(fileno(stdin), &response, 1); /* Use low-level input to avoid deadlock */ + if ((ret == 1) && (response == '\n')) { + ucs_debugger_attach(); + } else { + ucs_debug_freeze(); + } + } else { + ucs_log_fatal_error("Process frozen..."); + ucs_debug_freeze(); + } + + pthread_mutex_unlock(&lock); + } else { + ucs_debug_freeze(); + } + + return UCS_OK; +} + +static void ucs_error_signal_handler(int signo) +{ + ucs_debug_cleanup(); + ucs_log_flush(); + ucs_log_fatal_error("Caught signal %d (%s)", signo, strsignal(signo)); + if (signo != SIGINT && signo != SIGTERM) { + ucs_handle_error(); + } + raise(signo); +} + +void ucs_handle_error() +{ + ucs_status_t status; + + switch (ucs_global_opts.handle_errors) { + case UCS_HANDLE_ERROR_DEBUG: + status = ucs_debugger_attach(); + if (status == UCS_OK) { + break; + } + /* Fall thru */ + + case UCS_HANDLE_ERROR_FREEZE: + status = ucs_error_freeze(); + if (status == UCS_OK) { + break; + } + /* Fall thru */ + + case UCS_HANDLE_ERROR_BACKTRACE: + ucs_debug_print_backtrace(stderr, 2); + break; + + default: + break; + } +} + +static void ucs_debug_signal_handler(int signo) +{ + ucs_log_flush(); + + ucs_log_fatal_error("Got debug signal, raising log level", + ucs_get_host_name(), getpid()); + ucs_global_opts.log_level = UCS_LOG_LEVEL_TRACE_DATA; +} + +static void ucs_set_signal_handler(__sighandler_t handler) +{ + int i; + + for (i = 0; i < ucs_global_opts.error_signals.count; ++i) { + signal(ucs_global_opts.error_signals.signals[i], handler); + } +} + +ucs_status_t ucs_debug_lookup_address(void *address, ucs_debug_address_info_t *info) +{ + struct dl_address_search dl; + + dl.address = (unsigned long)address; + if (!dl_lookup_address(&dl)) { + return UCS_ERR_NO_ELEM; + } + + memset(info, 0, sizeof(*info)); + info->file.base = dl.base; + ucs_expand_path(dl.filename, info->file.path, sizeof(info->file.path)); + + ucs_debug_get_line_info(dl.filename, dl.base, (unsigned long)address, info); + return UCS_OK; +} + +static struct dl_address_search *ucs_debug_get_lib_info() +{ + static struct dl_address_search dl = {0, NULL, 0}; + + if (dl.address == 0) { + dl.address = (unsigned long)&ucs_debug_get_lib_info; + if (!dl_lookup_address(&dl)) { + dl.filename = NULL; + dl.base = 0; + } + } + + /* If we failed to look up the address, return NULL */ + return (dl.filename == NULL || dl.base == 0) ? NULL : &dl; +} + +const char *ucs_debug_get_lib_path() +{ + static char ucs_lib_path[256] = {0}; + struct dl_address_search *dl; + + if (!strlen(ucs_lib_path)) { + dl = ucs_debug_get_lib_info(); + if (dl != NULL) { + ucs_expand_path(dl->filename, ucs_lib_path, sizeof(ucs_lib_path)); + } + } + + return ucs_lib_path; +} + +unsigned long ucs_debug_get_lib_base_addr() +{ + struct dl_address_search *dl = ucs_debug_get_lib_info(); + return (dl == NULL) ? 0 : dl->base; +} + +void ucs_debug_init() +{ + if (ucs_global_opts.handle_errors > UCS_HANDLE_ERROR_NONE) { + ucs_set_signal_handler(ucs_error_signal_handler); + } + if (ucs_global_opts.debug_signo > 0) { + signal(ucs_global_opts.debug_signo, ucs_debug_signal_handler); + } + +#ifdef HAVE_DETAILED_BACKTRACE + bfd_init(); +#endif +} + +void ucs_debug_cleanup() +{ + if (ucs_global_opts.handle_errors > UCS_HANDLE_ERROR_NONE) { + ucs_set_signal_handler(SIG_DFL); + } + if (ucs_global_opts.debug_signo > 0) { + signal(ucs_global_opts.debug_signo, SIG_DFL); + } +} diff --git a/src/ucs/debug/debug.h b/src/ucs/debug/debug.h new file mode 100644 index 00000000000..0aa3e289a75 --- /dev/null +++ b/src/ucs/debug/debug.h @@ -0,0 +1,104 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_DEBUG_H_ +#define UCS_DEBUG_H_ + +#include +#include +#include + + +/** + * Information about an address in the code. + */ +typedef struct ucs_debug_address_info { + struct { + char path[512]; /* Binary file path */ + unsigned long base; /* Binary file load base */ + } file; + char function[128]; /* Function name */ + char source_file[512]; /* Source file path */ + unsigned line_number; /* Line number */ +} ucs_debug_address_info_t; + + +extern const char *ucs_state_detail_level_names[]; +extern const char *ucs_signal_names[]; + + +/** + * Initialize UCS debugging subsystem. + */ +void ucs_debug_init(); + + +/** + * Cleanup UCS debugging subsystem. + */ +void ucs_debug_cleanup(); + + +/** + * Get information about an address in the code of the current program. + * @param address Address to look up. + * @param info Filled with information about the given address. Source file + * and line number are filled only if the binary file was compiled + * with debug information, and UCS was configured with detailed + * backtrace enabled. + * @return UCS_ERR_NO_ELEM if the address is not found, UCS_OK otherwise. + */ +ucs_status_t ucs_debug_lookup_address(void *address, ucs_debug_address_info_t *info); + + +/** + * @return Full path to current library. + */ +const char *ucs_debug_get_lib_path(); + + +/** + * @return UCS library loading address. + */ +unsigned long ucs_debug_get_lib_base_addr(); + + +/** + * Get debug information. + * + * @param filename Object file to get information from. + * @param base Load address of the file. + * @param info Filled with debug information. + */ +void ucs_debug_get_line_info(const char *filename, unsigned long base, + unsigned long address, ucs_debug_address_info_t *info); + + +/** + * Print backtrace to an output stream. + * + * @param stream Stream to print to. + * @param strip How many frames to strip. + */ +void ucs_debug_print_backtrace(FILE *stream, int strip); + + +/** + * Called when UCS detects a fatal error and provides means to debug the current + * state of UCS. + */ +void ucs_handle_error(); + + +/** + * @return Name of a symbol which begins in the given address, or NULL if + * not found. + */ +const char *ucs_debug_get_symbol_name(void *address, char *buffer, size_t max); + + +#endif diff --git a/src/ucs/debug/instrument.c b/src/ucs/debug/instrument.c new file mode 100644 index 00000000000..2fe9e6dbe0a --- /dev/null +++ b/src/ucs/debug/instrument.c @@ -0,0 +1,156 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#include + +#include +#include +#include +#include + +#if HAVE_INSTRUMENTATION + +ucs_instrument_context_t ucs_instr_ctx; + +static void ucs_instrument_write_records(ucs_instrument_record_t *from, + ucs_instrument_record_t *to) +{ + ssize_t written; + size_t size; + + size = (char*)to - (char*)from; + written = write(ucs_instr_ctx.fd, (void*)from, size); + if (written < 0) { + ucs_warn("failed to write %Zu bytes to instrumentation file: %m", size); + } else if (size != written) { + ucs_warn("wrote only %Zd of %Zu bytes to instrumentation file: %m", written, size); + } +} + +static void ucs_instrument_fill_header(ucs_instrument_header_t *header) +{ + memset(header, 0, sizeof *header); + + /* Library */ + header->ucs_lib.base = ucs_debug_get_lib_base_addr(); + strncpy(header->ucs_lib.path, ucs_debug_get_lib_path(), sizeof(header->ucs_lib.path) - 1); + if (strlen(header->ucs_lib.path)) { + header->ucs_lib.chksum = ucs_file_checksum(header->ucs_lib.path); + } + + /* Process */ + ucs_read_file(header->app.cmdline, sizeof(header->app.cmdline), 1, "/proc/self/cmdline"); + header->app.pid = getpid(); + strncpy(header->app.hostname, ucs_get_host_name(), sizeof(header->app.hostname) - 1); + + /* Samples */ + header->num_records = ucs_min(ucs_instr_ctx.count - header->record_offset, + ucs_instr_ctx.end - ucs_instr_ctx.start); + header->record_offset = ucs_instr_ctx.count - header->num_records; + header->start_time = ucs_instr_ctx.start_time; + header->one_second = ucs_time_from_sec(1.0); +} + +static void ucs_instrument_write() +{ + ucs_instrument_header_t header; + + /* write header */ + ucs_instrument_fill_header(&header); + if (write(ucs_instr_ctx.fd, &header, sizeof(header)) < sizeof(header)) { + ucs_warn("failed to write instrument header"); + } + + /* write records */ + if (header.record_offset > 0) { + ucs_instrument_write_records(ucs_instr_ctx.current, ucs_instr_ctx.end); + } + ucs_instrument_write_records(ucs_instr_ctx.start, ucs_instr_ctx.current); +} + +void ucs_instrument_init() +{ + char fullpath[1024] = {0}; + char filename[1024] = {0}; + size_t num_records; + + if (strlen(ucs_global_opts.instrument_file) == 0) { + goto disable; + } + + ucs_fill_filename_template(ucs_global_opts.instrument_file, filename, sizeof(filename)); + ucs_expand_path(filename, fullpath, sizeof(fullpath) - 1); + + ucs_instr_ctx.fd = open(fullpath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (ucs_instr_ctx.fd < 0) { + ucs_warn("failed to open %s for writing: %m", fullpath); + goto disable; + } + + num_records = ucs_global_opts.instrument_max_size / sizeof(ucs_instrument_record_t); + ucs_instr_ctx.start = calloc(num_records, sizeof(ucs_instrument_record_t)); + if (ucs_instr_ctx.start == NULL) { + ucs_warn("failed to allocate instrumentation buffer"); + goto disable_close_file; + } + + ucs_instr_ctx.enable = 1; + ucs_instr_ctx.end = ucs_instr_ctx.start + num_records; + ucs_instr_ctx.current = ucs_instr_ctx.start; + ucs_instr_ctx.count = 0; + ucs_instr_ctx.start_time = ucs_get_time(); + + ucs_info("saving instrumentation records to %s", fullpath); + return; + +disable_close_file: + close(ucs_instr_ctx.fd); +disable: + ucs_instr_ctx.enable = 0; + ucs_trace("instrumentation is disabled"); +} + +void ucs_instrument_cleanup() +{ + if (!ucs_instr_ctx.enable) { + return; + } + + ucs_instrument_write(); + close(ucs_instr_ctx.fd); + free(ucs_instr_ctx.start); +} + +void __ucs_instrument_record(uint64_t location, uint64_t lparam, uint32_t wparam) +{ + ucs_instrument_context_t *ctx = &ucs_instr_ctx; + ucs_instrument_record_t *current = ctx->current; + + current->timestamp = ucs_get_time(); + current->lparam = lparam; + current->wparam = wparam; + current->location = (uint32_t)location; /* chop off high dword */ + + ++ctx->count; + ++ctx->current; + if (ctx->current >= ctx->end) { + ctx->current = ctx->start; + } +} + +#else + +void ucs_instrument_init() +{ +} + +void ucs_instrument_cleanup() +{ +} + +#endif diff --git a/src/ucs/debug/instrument.h b/src/ucs/debug/instrument.h new file mode 100644 index 00000000000..52a671f4db1 --- /dev/null +++ b/src/ucs/debug/instrument.h @@ -0,0 +1,121 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#ifndef INSTRUMENT_H_ +#define INSTRUMENT_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + + +/** + * Initialize instrumentation. + */ +void ucs_instrument_init(); + +/** + * Save and cleanup instrumentation. + */ +void ucs_instrument_cleanup(); + + +/** + * Instrumentation file header + */ +typedef struct ucs_instrument_header { + struct { + char path[1024]; /* UCS library path for location information */ + uint32_t chksum; /* UCS library checksum */ + unsigned long base; /* UCS library loading base */ + } ucs_lib; + + struct { + char cmdline[1024]; /* Command line */ + int pid; /* Process ID */ + char hostname[40]; /* Host name */ + } app; + + size_t num_records; /* Number of records in the file */ + size_t record_offset; /* File starts from the n-th record in the program */ + ucs_time_t start_time; /* Time when application has started */ + ucs_time_t one_second; /* How much time is one second on the sampled machine */ +} ucs_instrument_header_t; + + +typedef struct ucs_instrument_record { + uint64_t timestamp; + uint64_t lparam; + uint32_t wparam; + uint32_t location; +} ucs_instrument_record_t; + + +typedef struct ucs_instrument_context { + int enable; + ucs_time_t start_time; + ucs_instrument_record_t *start, *end; + ucs_instrument_record_t *current; + size_t count; + int fd; +} ucs_instrument_context_t; + + +/* Expand a 32-bit location address to full size address, assuming library + * size is <2G. + * @param location Abbreviated 32-bit location + * @param base UCS library load base address. + */ +static inline unsigned long ucs_instrument_expand_location(uint32_t location, + unsigned long base) +{ + uint32_t offset = (location - (uint32_t)base) & UCS_MASK(31); + return base + offset; +} + + +#if HAVE_INSTRUMENTATION + +/* + * Global instrumentation context. + */ +extern ucs_instrument_context_t ucs_instr_ctx; + + +void __ucs_instrument_record(uint64_t location, uint64_t lparam, uint32_t wparam); + + +/* + * Helper macros + */ +#define UCS_MAKE_LABEL(uniq) \ + label_##uniq +#define UCS_INSTRUMENT_RECORD_(uniq, lparam, wparam, ...) \ + do { \ + if (ucs_instr_ctx.enable) do { \ + UCS_MAKE_LABEL(uniq):\ + __ucs_instrument_record((uint64_t)&&UCS_MAKE_LABEL(uniq), (uint64_t)(lparam), (uint32_t)(wparam)); \ + } while (0); \ + } while (0) + +/* + * Main instrumentation macro + */ +#define UCS_INSTRUMENT_RECORD(...) \ + UCS_INSTRUMENT_RECORD_(UCS_PP_UNIQUE_ID, ## __VA_ARGS__, 0, 0) + +#else + +#define UCS_INSTRUMENT_RECORD(...) do {} while (0) + +#endif + + +#endif diff --git a/src/ucs/debug/log.c b/src/ucs/debug/log.c new file mode 100644 index 00000000000..d965d1c85b6 --- /dev/null +++ b/src/ucs/debug/log.c @@ -0,0 +1,326 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "log.h" +#include "debug.h" + +#include +#include + +const char *ucs_log_level_names[] = { + [UCS_LOG_LEVEL_FATAL] = "FATAL", + [UCS_LOG_LEVEL_ERROR] = "ERROR", + [UCS_LOG_LEVEL_WARN] = "WARN", + [UCS_LOG_LEVEL_INFO] = "INFO", + [UCS_LOG_LEVEL_DEBUG] = "DEBUG", + [UCS_LOG_LEVEL_TRACE] = "TRACE", + [UCS_LOG_LEVEL_TRACE_REQ] = "REQ", + [UCS_LOG_LEVEL_TRACE_DATA] = "DATA", + [UCS_LOG_LEVEL_TRACE_ASYNC] = "ASYNC", + [UCS_LOG_LEVEL_TRACE_FUNC] = "FUNC", + [UCS_LOG_LEVEL_TRACE_POLL] = "POLL", + [UCS_LOG_LEVEL_LAST] = NULL +}; + +static int ucs_log_initialized = 0; +static char ucs_log_hostname[256] = {0}; +static int ucs_log_pid = 0; +static FILE *ucs_log_file = NULL; +static int ucs_log_file_close = 0; +static unsigned threads_count = 0; +static pthread_spinlock_t threads_lock = 0; +static pthread_t threads[128]; + + +int get_thread_num(void) +{ + pthread_t self = pthread_self(); + unsigned i; + + for (i = 0; i < threads_count; ++i) { + if (threads[i] == self) { + return i; + } + } + + pthread_spin_lock(&threads_lock); + + for (i = 0; i < threads_count; ++i) { + if (threads[i] == self) { + goto unlock_and_return_i; + } + } + + if (threads_count >= sizeof(threads) / sizeof(threads[0])) { + i = -1; + goto unlock_and_return_i; + } + + i = threads_count; + ++threads_count; + threads[i] = self; + +unlock_and_return_i: + pthread_spin_unlock(&threads_lock); + return i; +} + +void ucs_log_early_init() +{ + ucs_log_initialized = 0; + ucs_log_hostname[0] = 0; + ucs_log_pid = getpid(); + ucs_log_file = NULL; + ucs_log_file_close = 0; + threads_count = 0; + pthread_spin_init(&threads_lock, 0); +} + +void ucs_log_init() +{ + const char *next_token; + + if (ucs_log_initialized) { + return; + } + + ucs_log_initialized = 1; /* Set this to 1 immediately to avoid infinite recursion */ + + strcpy(ucs_log_hostname, ucs_get_host_name()); + + ucs_log_file = stdout; + ucs_log_file_close = 0; + + if (strlen(ucs_global_opts.log_file) != 0) { + ucs_open_output_stream(ucs_global_opts.log_file, &ucs_log_file, + &ucs_log_file_close, &next_token); + } + + ucs_debug("%s loaded at 0x%lx", ucs_debug_get_lib_path(), + ucs_debug_get_lib_base_addr()); +} + +void ucs_log_cleanup() +{ + ucs_log_flush(); + if (ucs_log_file_close) { + fclose(ucs_log_file); + } + ucs_log_file = NULL; +} + +void ucs_log_flush() +{ + if (ucs_log_file != NULL) { + fflush(ucs_log_file); + fsync(fileno(ucs_log_file)); + } +} + +void __ucs_vlog(const char *file, unsigned line, const char *function, + unsigned level, const char *prefix, const char *message, + va_list ap) +{ + size_t buffer_size = ucs_global_opts.log_buffer_size; + const char *short_file; + struct timeval tv; + size_t length; + char *buf; + char *valg_buf; + + if (level > ucs_log_level) { + return; + } + + buf = alloca(buffer_size + 1); + buf[buffer_size] = 0; + + strncpy(buf, prefix, buffer_size); + length = strlen(buf); + vsnprintf(buf + length, buffer_size - length, message, ap); + + gettimeofday(&tv, NULL); + + short_file = strrchr(file, '/'); + short_file = (short_file == NULL) ? file : short_file + 1; + + if (RUNNING_ON_VALGRIND) { + valg_buf = alloca(buffer_size + 1); + snprintf(valg_buf, buffer_size, + "[%lu.%06lu] %13s:%-4u %-4s %-5s %s\n", tv.tv_sec, tv.tv_usec, + short_file, line, "UCX", ucs_log_level_names[level], buf); + VALGRIND_PRINTF("%s", valg_buf); + } else if (ucs_log_initialized) { + fprintf(ucs_log_file, + "[%lu.%06lu] [%s:%-5d:%d] %13s:%-4u %-4s %-5s %s\n", + tv.tv_sec, tv.tv_usec, ucs_log_hostname, ucs_log_pid, get_thread_num(), + short_file, line, "UCX", ucs_log_level_names[level], buf); + } else { + fprintf(stdout, + "[%lu.%06lu] %13s:%-4u %-4s %-5s %s\n", + tv.tv_sec, tv.tv_usec, short_file, line, + "UCX", ucs_log_level_names[level], buf); + } + + /* flush the log file if the log_level of this message is fatal or error */ + if (level <= UCS_LOG_LEVEL_ERROR) { + ucs_log_flush(); + } + +} + +void __ucs_log(const char *file, unsigned line, const char *function, + unsigned level, const char *message, ...) +{ + va_list ap; + + va_start(ap, message); + __ucs_vlog(file, line, function, level, "", message, ap); + va_end(ap); +} + +void ucs_log_fatal_error(const char *fmt, ...) +{ + size_t buffer_size = ucs_global_opts.log_buffer_size; + FILE *stream = stderr; + char *buffer, *p; + va_list ap; + int ret; + + buffer = alloca(buffer_size + 1); + p = buffer; + + /* Print hostname:pid */ + snprintf(p, buffer_size, "[%s:%-5d:%d] ", ucs_log_hostname, ucs_log_pid, + get_thread_num()); + buffer_size -= strlen(p); + p += strlen(p); + + /* Print rest of the message */ + va_start(ap, fmt); + vsnprintf(p, buffer_size, fmt, ap); + va_end(ap); + buffer_size -= strlen(p); + p += strlen(p); + + /* Newline */ + snprintf(p, buffer_size, "\n"); + + /* Flush stderr, and write the message directly to the pipe */ + fflush(stream); + ret = write(fileno(stream), buffer, strlen(buffer)); + (void)ret; +} + +void __ucs_abort(const char *file, unsigned line, const char *function, + const char *message, ...) +{ + size_t buffer_size = ucs_global_opts.log_buffer_size; + const char *short_file; + char *buffer; + va_list ap; + + buffer = alloca(buffer_size + 1); + va_start(ap, message); + vsnprintf(buffer, buffer_size, message, ap); + va_end(ap); + + short_file = strrchr(file, '/'); + short_file = (short_file == NULL) ? file : short_file + 1; + ucs_log_fatal_error("%13s:%-4u %s", short_file, line, buffer); + + ucs_log_flush(); + ucs_debug_cleanup(); + ucs_handle_error(); + abort(); +} + +/** + * Print a bitmap as a list of ranges. + * + * @param n Number equivalent to the first bit in the bitmap. + * @param bitmap Compressed array of bits. + * @param length Number of bits in the bitmap. + */ +const char *ucs_log_bitmap_to_str(unsigned n, uint8_t *bitmap, size_t length) +{ + static char buf[512] = {0}; + int first, in_range; + unsigned prev = 0, end = 0; + char *p, *endp; + size_t i; + + p = buf; + endp = buf + sizeof(buf) - 4; + + first = 1; + in_range = 0; + for (i = 0; i < length; ++i) { + if (bitmap[i / 8] & UCS_BIT(i % 8)) { + if (first) { + p += snprintf(p, endp - p, "%d", n); + if (p > endp) { + goto overflow; + } + } else if (n == prev + 1) { + in_range = 1; + end = n; + } else { + if (in_range) { + p += snprintf(p, endp - p, "-%d", end); + if (p > endp) { + goto overflow; + } + } + in_range = 0; + p += snprintf(p, endp - p, ",%d", n); + if (p > endp) { + goto overflow; + } + } + first = 0; + prev = n; + } + ++n; + } + if (in_range) { + p += snprintf(p, endp - p, "-%d", end); + if (p > endp) { + goto overflow; + } + } + return buf; + +overflow: + strcpy(p, "..."); + return buf; +} + + +void ucs_log_dump_hex(const void* data, size_t length, char *buf, size_t max) +{ + static const char hexchars[] = "0123456789abcdef"; + char *p, *endp; + uint8_t value; + size_t i; + + p = buf; + endp = buf + max - 2; + + i = 0; + while ((p < endp) && (i < length)) { + if (((i % 4) == 0) && (i > 0)) { + *(p++) = ':'; + } + value = *(uint8_t*)(data + i); + p[0] = hexchars[value / 16]; + p[1] = hexchars[value % 16]; + p += 2; + ++i; + } + *p = 0; +} diff --git a/src/ucs/debug/log.h b/src/ucs/debug/log.h index 2124bb00edf..4d986e0e74a 100644 --- a/src/ucs/debug/log.h +++ b/src/ucs/debug/log.h @@ -1,31 +1,114 @@ /** -* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. * * $COPYRIGHT$ * $HEADER$ */ -#ifndef UCS_DEBUG_LOG_H_ -#define UCS_DEBUG_LOG_H_ +#ifndef UCS_LOG_H_ +#define UCS_LOG_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" /* Defines UCS_MAX_LOG_LEVEL */ +#endif + +#include +#include +#include + + +#define ucs_log_level (ucs_global_opts.log_level) + +#define ucs_log(_level, _message, ...) \ + do { \ + if (ucs_unlikely(((_level) <= UCS_MAX_LOG_LEVEL) && ((_level) <= ucs_log_level))) { \ + __ucs_log(__FILE__, __LINE__, __FUNCTION__, (_level), \ + _message, ## __VA_ARGS__); \ + } \ + } while (0) + + +#define ucs_assert_always(_expression) \ + do { \ + if (!ucs_likely(_expression)) { \ + __ucs_abort(__FILE__, __LINE__, __FUNCTION__, "Assertion `%s' failed", \ + #_expression); \ + } \ + } while (0) + +#define ucs_assertv_always(_expression, _fmt, ...) \ + do { \ + if (!ucs_likely(_expression)) { \ + __ucs_abort(__FILE__, __LINE__, __FUNCTION__, "Assertion `%s' failed: "_fmt, \ + #_expression, ## __VA_ARGS__); \ + } \ + } while (0) + +#if ENABLE_ASSERT + +#define ucs_assert(_expression) ucs_assert_always(_expression) +#define ucs_assertv(_expression, _fmt, ...) ucs_assertv_always(_expression, _fmt, ## __VA_ARGS__) + +#else + +#define ucs_assert(_expression) +#define ucs_assertv(_expression, _fmt, ...) + +#endif + + +#define ucs_fatal(_message, ...) \ + __ucs_abort(__FILE__, __LINE__, __FUNCTION__, "Fatal: " _message, ## __VA_ARGS__) + +#define ucs_error(_message, ...) ucs_log(UCS_LOG_LEVEL_ERROR, _message, ## __VA_ARGS__) +#define ucs_warn(_message, ...) ucs_log(UCS_LOG_LEVEL_WARN, _message, ## __VA_ARGS__) +#define ucs_info(_message, ...) ucs_log(UCS_LOG_LEVEL_INFO, _message, ## __VA_ARGS__) +#define ucs_debug(_message, ...) ucs_log(UCS_LOG_LEVEL_DEBUG, _message, ## __VA_ARGS__) +#define ucs_trace(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE, _message, ## __VA_ARGS__) +#define ucs_trace_req(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE_REQ, _message, ## __VA_ARGS__) +#define ucs_trace_data(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE_DATA, _message, ## __VA_ARGS__) +#define ucs_trace_async(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE_ASYNC, _message, ## __VA_ARGS__) +#define ucs_trace_func(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE_FUNC, "%s("_message")", __FUNCTION__, ## __VA_ARGS__) +#define ucs_trace_poll(_message, ...) ucs_log(UCS_LOG_LEVEL_TRACE_POLL, "%s("_message")", __FUNCTION__, ## __VA_ARGS__) + + +extern const char *ucs_log_level_names[]; +extern const char *ucs_log_category_names[]; +struct ibv_sge; +struct ibv_wc; +struct ibv_send_wr; + +/** + * Initialize logging subsystem. + */ +void ucs_log_early_init(); +void ucs_log_init(); +void ucs_log_cleanup(); /** - * Logging levels + * Flush out logs. */ -typedef enum { - UCS_LOG_LEVEL_FATAL, - UCS_LOG_LEVEL_ERROR, - UCS_LOG_LEVEL_WARN, - UCS_LOG_LEVEL_INFO, - UCS_LOG_LEVEL_DEBUG, - UCS_LOG_LEVEL_TRACE, - UCS_LOG_LEVEL_TRACE_REQ, - UCS_LOG_LEVEL_TRACE_DATA, - UCS_LOG_LEVEL_TRACE_ASYNC, - UCS_LOG_LEVEL_TRACE_FUNC, - UCS_LOG_LEVEL_TRACE_POLL, - UCS_LOG_LEVEL_LAST -} ucs_log_level_t; +void ucs_log_flush(); + +void __ucs_log(const char *file, unsigned line, const char *function, + unsigned level, const char *message, ...) + UCS_F_PRINTF(5, 6); + +void __ucs_vlog(const char *file, unsigned line, const char *function, + unsigned level, const char *prefix, const char *message, + va_list ap); + +void __ucs_abort(const char *file, unsigned line, const char *function, + const char *message, ...) + UCS_F_NORETURN UCS_F_PRINTF(4, 5); + +void ucs_log_fatal_error(const char *fmt, ...); + +const char *ucs_log_bitmap_to_str(unsigned n, uint8_t *bitmap, size_t length); + +void ucs_log_dump_hex(const void* data, size_t length, char *buf, size_t max); #endif + diff --git a/src/ucs/debug/memtrack.c b/src/ucs/debug/memtrack.c new file mode 100644 index 00000000000..fa7471a5874 --- /dev/null +++ b/src/ucs/debug/memtrack.c @@ -0,0 +1,493 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if ENABLE_MEMTRACK + +#define MEMTRACK_BUFFER_MAGIC 0x1ee7beefa880feed +#define MEMTRACK_DUMP_FORMAT_STRING ("%22s: size: %9lu / %9lu\tcount: %9lu / %9lu\n") +#define UCS_MEMTRACK_ENTRY_HASH_SIZE 127 + + +typedef struct ucs_memtrack_context { + int enabled; + pthread_mutex_t lock; + ucs_memtrack_entry_t *entries[UCS_MEMTRACK_ENTRY_HASH_SIZE]; + UCS_STATS_NODE_DECLARE(stats); +} ucs_memtrack_context_t; + + +/* Global context for tracking all the memory */ +static ucs_memtrack_context_t ucs_memtrack_context = { + .enabled = 0, + .lock = PTHREAD_MUTEX_INITIALIZER +}; + + +static inline unsigned ucs_memtrack_entry_hash(ucs_memtrack_entry_t *entry) +{ + return entry->origin % UCS_MEMTRACK_ENTRY_HASH_SIZE; +} + +static inline int ucs_memtrack_entry_compare(ucs_memtrack_entry_t *entry1, + ucs_memtrack_entry_t *entry2) +{ + if (entry1->origin != entry2->origin) { + return (int)entry1->origin - (int)entry2->origin; + } else { + return strcmp(entry1->alloc_name, entry2->alloc_name); + } +} + + +SGLIB_DEFINE_LIST_PROTOTYPES(ucs_memtrack_entry_t, ucs_memtrack_entry_compare, next) +SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(ucs_memtrack_entry_t, + UCS_MEMTRACK_ENTRY_HASH_SIZE, + ucs_memtrack_entry_hash) + +#if ENABLE_STATS +static ucs_stats_class_t ucs_memtrack_stats_class = { + .name = "memtrack", + .num_counters = UCS_MEMTRACK_STAT_LAST, + .counter_names = { + [UCS_MEMTRACK_STAT_ALLOCATION_COUNT] = "alloc_cnt", + [UCS_MEMTRACK_STAT_ALLOCATION_SIZE] = "alloc_size" + } +}; +#endif + +static inline ucs_memtrack_entry_t* ucs_memtrack_create_entry(const char* alloc_name, + unsigned origin) +{ + ucs_memtrack_entry_t *new_entry = malloc(sizeof(*new_entry)); + if (new_entry == NULL) { + return NULL; + } + + new_entry->current_size = 0; + new_entry->peak_size = 0; + new_entry->current_count = 0; + new_entry->peak_count = 0; + + new_entry->origin = origin; + new_entry->alloc_name = alloc_name ? strdup(alloc_name) : NULL; + sglib_hashed_ucs_memtrack_entry_t_add(ucs_memtrack_context.entries, new_entry); + return new_entry; +} + +void ucs_memtrack_record_alloc(ucs_memtrack_buffer_t* buffer, size_t size + UCS_MEMTRACK_ARG) +{ + ucs_memtrack_entry_t *new_entry, search = {0}; + + if (!ucs_memtrack_context.enabled) { + return; + } + + ucs_assert(buffer != NULL); + ucs_assert(alloc_name != NULL); + ucs_assert(ucs_memtrack_context.entries != NULL); // context initialized + pthread_mutex_lock(&ucs_memtrack_context.lock); + + search.origin = origin; + search.alloc_name = (char*)((void*)alloc_name); + new_entry = sglib_hashed_ucs_memtrack_entry_t_find_member(ucs_memtrack_context.entries, + &search); + if (new_entry == NULL) { + new_entry = ucs_memtrack_create_entry(alloc_name, origin); + if (new_entry == NULL) { + goto out_unlock; + } + } + + ucs_assert(0 == strcmp(alloc_name, new_entry->alloc_name)); + buffer->magic = MEMTRACK_BUFFER_MAGIC; + buffer->length = size; + buffer->offset = 0; + buffer->entry = new_entry; + + new_entry->current_size += size; + UCS_STATS_UPDATE_COUNTER(ucs_memtrack_context.stats, UCS_MEMTRACK_STAT_ALLOCATION_SIZE, size); + new_entry->peak_size = ucs_max(new_entry->peak_size, new_entry->current_size); + + new_entry->current_count++; + UCS_STATS_UPDATE_COUNTER(ucs_memtrack_context.stats, UCS_MEMTRACK_STAT_ALLOCATION_COUNT, 1); + new_entry->peak_count = ucs_max(new_entry->peak_count, new_entry->current_count); + +out_unlock: + pthread_mutex_unlock(&ucs_memtrack_context.lock); +} + +ucs_memtrack_entry_t* ucs_memtrack_record_dealloc(ucs_memtrack_buffer_t *buffer) +{ + ucs_memtrack_entry_t *res; + if (!ucs_memtrack_context.enabled) { + return NULL; + } + + pthread_mutex_lock(&ucs_memtrack_context.lock); + ucs_assert(buffer->magic == MEMTRACK_BUFFER_MAGIC); +#if ENABLE_ASSERT + buffer->magic = MEMTRACK_BUFFER_MAGIC + 1; /* protect from double free */ +#endif + + res = buffer->entry; + ucs_assert(res->current_size >= buffer->length); + res->current_size -= buffer->length; + ucs_assert(res->current_count >= 1); + res->current_count--; + + pthread_mutex_unlock(&ucs_memtrack_context.lock); + return res; +} + +void *ucs_memtrack_calloc(size_t nmemb, size_t size UCS_MEMTRACK_ARG) +{ + ucs_memtrack_buffer_t *res; + res = calloc(1, nmemb * size + (ucs_memtrack_context.enabled ? sizeof(*res) : 0)); + if ((res == NULL) || (!ucs_memtrack_context.enabled)) { + return res; + } + + ucs_memtrack_record_alloc(res, nmemb * size, alloc_name, origin); + return res + 1; +} + +void *ucs_memtrack_malloc(size_t size UCS_MEMTRACK_ARG) +{ + ucs_memtrack_buffer_t *res; + res = malloc(size + (ucs_memtrack_context.enabled ? sizeof(*res) : 0)); + if ((res == NULL) || (!ucs_memtrack_context.enabled)) { + return res; + } + + ucs_memtrack_record_alloc(res, size, alloc_name, origin); + return res + 1; +} + +void *ucs_memtrack_memalign(size_t boundary, size_t size UCS_MEMTRACK_ARG) +{ + size_t offset = 0; + ucs_memtrack_buffer_t *res; + if (!ucs_memtrack_context.enabled) { + return memalign(boundary, size); + } + + if (boundary > sizeof(*res)) { + res = memalign(boundary, size + boundary); + offset = boundary - sizeof(*res); + } else { + if (sizeof(*res) % boundary != 0) { + offset = boundary - (sizeof(*res) % boundary); + } + res = memalign(boundary, size + sizeof(*res) + offset); + } + if ((res == NULL) || (!ucs_memtrack_context.enabled)) { + return res; + } + + res = (void*)res + offset; + ucs_memtrack_record_alloc(res, size, alloc_name, origin); + res->offset = offset; + return res + 1; +} + +void ucs_memtrack_free(void *ptr) +{ + ucs_memtrack_buffer_t *buffer = (ucs_memtrack_buffer_t*)ptr - 1; + if (!ucs_memtrack_context.enabled) { + free(ptr); + } else { + if (ptr == NULL) { + return; + } + ucs_memtrack_record_dealloc(buffer); + free((void*)buffer - buffer->offset); + } +} + +void *ucs_memtrack_mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset UCS_MEMTRACK_ARG) +{ + ucs_memtrack_buffer_t *res; + if ((flags & MAP_FIXED) || !(prot & PROT_WRITE)) { + return NULL; + } + + res = mmap(addr, length + (ucs_memtrack_context.enabled ? sizeof(*res) : 0), + prot, flags, fd, offset); + if ((res == NULL) || (!ucs_memtrack_context.enabled)) { + return res; + } + + if (fd > 0) { + memmove(res + 1, res, length); + } + + ucs_memtrack_record_alloc(res, length UCS_MEMTRACK_VAL); + return res + 1; +} + +#ifdef __USE_LARGEFILE64 +void *ucs_memtrack_mmap64(void *addr, size_t length, int prot, int flags, + int fd, uint64_t offset UCS_MEMTRACK_ARG) +{ + ucs_memtrack_buffer_t *res; + if ((flags & MAP_FIXED) || !(prot & PROT_WRITE)) { + return NULL; + } + + res = mmap64(addr, length + (ucs_memtrack_context.enabled ? sizeof(*res) : 0), + prot, flags, fd, offset); + if ((res == NULL) || (!ucs_memtrack_context.enabled)) { + return res; + } + + if (fd > 0) { + memmove(res + 1, res, length); + } + + ucs_memtrack_record_alloc(res, length UCS_MEMTRACK_VAL); + return res + 1; +} +#endif + +int ucs_memtrack_munmap(void *addr, size_t length) +{ + ucs_memtrack_buffer_t *buffer = (ucs_memtrack_buffer_t*)addr - 1; + if (!ucs_memtrack_context.enabled) { + return munmap(addr, length); + } + + ucs_assert(buffer->length == length); // Otherwise it's part of the buffer! + ucs_memtrack_record_dealloc(buffer); + return munmap((void*)buffer - buffer->offset, + length + sizeof(*buffer) + buffer->offset); +} + +void *ucs_memtrack_realloc(void *ptr, size_t size) +{ + ucs_memtrack_buffer_t *buffer = (ucs_memtrack_buffer_t*)ptr - 1; + ucs_memtrack_entry_t *entry; + + if (!ucs_memtrack_context.enabled) { + return realloc(ptr, size); + } + + entry = ucs_memtrack_record_dealloc(buffer); + buffer = realloc((void*)buffer - buffer->offset, size + sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + if (!ucs_memtrack_context.enabled) { + return buffer; + } + + if (entry != NULL) { + ucs_memtrack_record_alloc(buffer, size, entry->alloc_name, + entry->origin); + } + return buffer + 1; +} + +static unsigned ucs_memtrack_total_internal(ucs_memtrack_entry_t* total) +{ + struct sglib_hashed_ucs_memtrack_entry_t_iterator entry_it; + ucs_memtrack_entry_t *entry; + unsigned num_entries; + + if (!ucs_memtrack_context.enabled) { + return 0; + } + + num_entries = 0; + total->current_size = 0; + total->peak_size = 0; + total->current_count = 0; + total->peak_count = 0; + + for (entry = sglib_hashed_ucs_memtrack_entry_t_it_init(&entry_it, + ucs_memtrack_context.entries); + entry != NULL; + entry = sglib_hashed_ucs_memtrack_entry_t_it_next(&entry_it)) + { + total->current_size += entry->current_size; + total->peak_size += entry->peak_size; + total->current_count += entry->current_count; + total->peak_count += entry->peak_count; + ++num_entries; + } + + return num_entries; +} + +void ucs_memtrack_total(ucs_memtrack_entry_t* total) +{ + pthread_mutex_lock(&ucs_memtrack_context.lock); + ucs_memtrack_total_internal(total); + pthread_mutex_unlock(&ucs_memtrack_context.lock); +} + +static int ucs_memtrack_cmp_entries(const void *ptr1, const void *ptr2) +{ + const ucs_memtrack_entry_t *e1 = ptr1; + const ucs_memtrack_entry_t *e2 = ptr2; + + return (int)((ssize_t)e2->peak_size - (ssize_t)e1->peak_size); +} + +static void ucs_memtrack_dump_internal(FILE* output_stream) +{ + struct sglib_hashed_ucs_memtrack_entry_t_iterator entry_it; + ucs_memtrack_entry_t total = {0}; + ucs_memtrack_entry_t *entry, *all_entries; + unsigned num_entries, i; + + if (!ucs_memtrack_context.enabled) { + return; + } + + num_entries = ucs_memtrack_total_internal(&total); + + fprintf(output_stream, "%31s current / peak %16s current / peak\n", "", ""); + fprintf(output_stream, MEMTRACK_DUMP_FORMAT_STRING, "TOTAL", + total.current_size, total.peak_size, + total.current_count, total.peak_count); + + all_entries = malloc(sizeof(ucs_memtrack_entry_t) * num_entries); + + /* Copy all entries to one array */ + i = 0; + for (entry = sglib_hashed_ucs_memtrack_entry_t_it_init(&entry_it, + ucs_memtrack_context.entries); + entry != NULL; + entry = sglib_hashed_ucs_memtrack_entry_t_it_next(&entry_it)) + { + all_entries[i++] = *entry; + } + ucs_assert(i == num_entries); + + /* Sort the entries from large to small */ + qsort(all_entries, num_entries, sizeof(ucs_memtrack_entry_t), ucs_memtrack_cmp_entries); + for (i = 0; i < num_entries; ++i) { + entry = &all_entries[i]; + fprintf(output_stream, MEMTRACK_DUMP_FORMAT_STRING, entry->alloc_name, + entry->current_size, entry->peak_size, + entry->current_count, entry->peak_count); + } + + free(all_entries); +} + +void ucs_memtrack_dump(FILE* output_stream) +{ + pthread_mutex_lock(&ucs_memtrack_context.lock); + ucs_memtrack_dump_internal(output_stream); + pthread_mutex_unlock(&ucs_memtrack_context.lock); +} + +static void ucs_memtrack_generate_report() +{ + ucs_status_t status; + FILE* output_stream; + const char *next_token; + int need_close; + + status = ucs_open_output_stream(ucs_global_opts.memtrack_dest, &output_stream, + &need_close, &next_token); + if (status != UCS_OK) { + return; + } + + ucs_memtrack_dump_internal(output_stream); + if (need_close) { + fclose(output_stream); + } +} + +void ucs_memtrack_init() +{ + ucs_status_t status; + + ucs_assert(ucs_memtrack_context.enabled == 0); + + if (!strcmp(ucs_global_opts.memtrack_dest, "")) { + ucs_trace("memtrack disabled"); + ucs_memtrack_context.enabled = 0; + return; + } + + sglib_hashed_ucs_memtrack_entry_t_init(ucs_memtrack_context.entries); + status = UCS_STATS_NODE_ALLOC(&ucs_memtrack_context.stats, &ucs_memtrack_stats_class, NULL); + if (status != UCS_OK) { + return; + } + + ucs_debug("memtrack enabled"); + ucs_memtrack_context.enabled = 1; +} + +void ucs_memtrack_cleanup() +{ + struct sglib_hashed_ucs_memtrack_entry_t_iterator entry_it; + ucs_memtrack_entry_t *entry; + + if (!ucs_memtrack_context.enabled) { + return; + } + + pthread_mutex_lock(&ucs_memtrack_context.lock); + + ucs_memtrack_generate_report(); + + /* disable before releasing the stats node */ + ucs_memtrack_context.enabled = 0; + UCS_STATS_NODE_FREE(ucs_memtrack_context.stats); + for (entry = sglib_hashed_ucs_memtrack_entry_t_it_init(&entry_it, + ucs_memtrack_context.entries); + entry != NULL; + entry = sglib_hashed_ucs_memtrack_entry_t_it_next(&entry_it)) + { + sglib_hashed_ucs_memtrack_entry_t_delete(ucs_memtrack_context.entries, entry); + free(entry->alloc_name); + free(entry); + } + pthread_mutex_unlock(&ucs_memtrack_context.lock); +} + +int ucs_memtrack_is_enabled() +{ + return ucs_memtrack_context.enabled; +} + +SGLIB_DEFINE_LIST_FUNCTIONS(ucs_memtrack_entry_t, ucs_memtrack_entry_compare, next) +SGLIB_DEFINE_HASHED_CONTAINER_FUNCTIONS(ucs_memtrack_entry_t, + UCS_MEMTRACK_ENTRY_HASH_SIZE, + ucs_memtrack_entry_hash) + +#else + +void ucs_memtrack_init() +{ +} + +void ucs_memtrack_cleanup() +{ +} + +#endif diff --git a/src/ucs/debug/memtrack.h b/src/ucs/debug/memtrack.h new file mode 100644 index 00000000000..8521423eae0 --- /dev/null +++ b/src/ucs/debug/memtrack.h @@ -0,0 +1,254 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_MEMTRACK_H_ +#define UCS_MEMTRACK_H_ + +#include +#include +#include + + +enum { + UCS_MEMTRACK_STAT_ALLOCATION_COUNT, + UCS_MEMTRACK_STAT_ALLOCATION_SIZE, + UCS_MEMTRACK_STAT_LAST +}; + + +typedef struct ucs_memtrack_entry +{ + char* alloc_name; + unsigned origin; + + size_t current_size; + size_t peak_size; + + size_t current_count; + size_t peak_count; + + struct ucs_memtrack_entry* next; +} ucs_memtrack_entry_t; + + +typedef struct ucs_memtrack_buffer +{ + size_t magic; /* Make sure this buffer is "memtracked" */ + size_t length; /* length of user-requested buffer */ + size_t offset; /* Offset between result of memory allocation and the + location of this buffer struct (mainly for ucs_memalign) */ + + ucs_memtrack_entry_t *entry; +} ucs_memtrack_buffer_t; + + +#if ENABLE_MEMTRACK + +#define UCS_MEMTRACK_NAME(name) , name , __LINE__ +#define UCS_MEMTRACK_ARG , const char* alloc_name, unsigned origin +#define UCS_MEMTRACK_VAL , alloc_name, origin + +#define UCS_MEMTRACK_ADJUST_SIZE_BEFORE(ptr) {\ + if (ucs_memtrack_is_enabled()) *ptr += sizeof(ucs_memtrack_buffer_t);\ + } +#define UCS_MEMTRACK_ADJUST_SIZE_AFTER(ptr) {\ + if (ucs_memtrack_is_enabled()) *ptr -= sizeof(ucs_memtrack_buffer_t);\ + } + +#define ucs_calloc(nmemb, size, name) \ + ucs_memtrack_calloc(nmemb, size UCS_MEMTRACK_NAME(name)) +#define ucs_calloc_fwd ucs_memtrack_calloc +#define ucs_malloc(size, name) \ + ucs_memtrack_malloc(size UCS_MEMTRACK_NAME(name)) +#define ucs_malloc_cachealigned(size, name) \ + ucs_memtrack_memalign(UCS_SYS_CACHE_LINE_SIZE, size UCS_MEMTRACK_NAME(name)) +#define ucs_malloc_fwd ucs_memtrack_malloc +#define ucs_free(ptr) \ + ucs_memtrack_free(ptr) +#define ucs_realloc(ptr, size) \ + ucs_memtrack_realloc(ptr, size) +#define ucs_memalign(boundary, size, name) \ + ucs_memtrack_memalign(boundary, size UCS_MEMTRACK_NAME(name)) +#define ucs_memalign_fwd ucs_memtrack_memalign + +#define ucs_mmap(addr, length, prot, flags, fd, offset, name) \ + ucs_memtrack_mmap(addr, length, prot, flags, fd, offset UCS_MEMTRACK_NAME(name)) +#define ucs_mmap_fwd ucs_memtrack_mmap +#define ucs_mmap64(addr, length, prot, flags, fd, offset, name) \ + ucs_memtrack_mmap(addr, length, prot, flags, fd, offset UCS_MEMTRACK_NAME(name)) +#define ucs_munmap(addr, length) \ + ucs_memtrack_munmap(addr, length) + +#else + +#define UCS_MEMTRACK_NAME(name) +#define UCS_MEMTRACK_ARG +#define UCS_MEMTRACK_VAL +#define UCS_MEMTRACK_ADJUST_SIZE_BEFORE(ptr) +#define UCS_MEMTRACK_ADJUST_SIZE_AFTER(ptr) + +#define ucs_calloc(nmemb, size, name) \ + calloc(nmemb, size) +#define ucs_calloc_fwd calloc +#define ucs_malloc(size, name) \ + malloc(size) +#define ucs_malloc_cachealigned(size, name) \ + memalign(UCS_SYS_CACHE_LINE_SIZE, size) +#define ucs_malloc_fwd malloc +#define ucs_free(ptr) \ + free(ptr) +#define ucs_realloc(ptr, size) \ + realloc(ptr, size) +#define ucs_memalign(boundary, size, name) \ + memalign(boundary, size) +#define ucs_memalign_fwd memalign + +#define ucs_mmap(addr, length, prot, flags, fd, offset, name) \ + mmap(addr, length, prot, flags, fd, offset UCS_MEMTRACK_NAME(name)) +#define ucs_mmap_fwd mmap +#define ucs_mmap64(addr, length, prot, flags, fd, offset, name) \ + mmap(addr, length, prot, flags, fd, offset UCS_MEMTRACK_NAME(name)) +#define ucs_munmap(addr, length) \ + munmap(addr, length) + +#endif /* ENABLE_MEMTRACK */ + +#define UCS_MEMTRACK_ADJUST_PTR_BEFORE(ptr) UCS_MEMTRACK_ADJUST_SIZE_AFTER(ptr) +#define UCS_MEMTRACK_ADJUST_PTR_AFTER(ptr) UCS_MEMTRACK_ADJUST_SIZE_BEFORE(ptr) + +/** + * Start trakcing memory (or increment reference count). + */ +void ucs_memtrack_init(); + +/** + * Stop trakcing memory (or decrement reference count). + */ +void ucs_memtrack_cleanup(); + +/* + * Check if memtrack is enbled at the moment. + */ +int ucs_memtrack_is_enabled(); + +/** + * Print a summary of memory tracked so far. + * + * @param output Stream to direct output to. + */ +void ucs_memtrack_dump(FILE* output); + +/** + * Calculates the total of buffers currently tracked. + * + * @param total Entry (pre-allocated) to place results in. + */ +void ucs_memtrack_total(ucs_memtrack_entry_t* total); + +/** + * Memory allocation registration for tracking. + * + * @param ptr Pointer to note as allocated. + * @param size Size of the requested allocation. + * @param alloc_name Name for this allocation command. + * + */ +void ucs_memtrack_record_alloc(ucs_memtrack_buffer_t* buffer, size_t size + UCS_MEMTRACK_ARG); + +/** + * Memory deallocation registration for tracking. + * + * @param ptr Pointer to note as deallocated. + */ +ucs_memtrack_entry_t* ucs_memtrack_record_dealloc(ucs_memtrack_buffer_t* buffer); + +/** + * Clear memory allocation request. + * + * @param nmemb Amount of allocated slots requested. + * @param size Size of allocation slots requested. + * @param alloc_name Name for this allocation command. + * @param origin Origin of the allocation command (used for id). + */ +void *ucs_memtrack_calloc(size_t nmemb, size_t size UCS_MEMTRACK_ARG); + +/** + * Memory allocation request. + * + * @param size Size of allocation requested. + * @param alloc_name Name for this allocation command. + * @param origin Origin of the allocation command (used for id). + */ +void *ucs_memtrack_malloc(size_t size UCS_MEMTRACK_ARG); + +/** + * Memory deallocation request. + * + * @param ptr Pointer for note as allocated. + */ +void ucs_memtrack_free(void *ptr); + +/** + * Memory reallocation request. + * + * @param ptr Pointer to reallocate. + * @param size Size of the requested allocation. + */ +void *ucs_memtrack_realloc(void *ptr, size_t size); + +/** + * Memory alignment request. + * + * @param boundary Size to align memory to. + * @param size Size of the requested allocation. + * @param alloc_name Name for this allocation command. + * @param origin Origin of the allocation command (used for id). + */ +void *ucs_memtrack_memalign(size_t boundary, size_t size UCS_MEMTRACK_ARG); + +/** + * Memory mapping request. + * + * @param addr Address hint for mapping. + * @param length Size of the requested allocation. + * @param prot Memory protection mask. + * @param flags Other flags. + * @param fd File to map. + * @param offset Offset within the file. + * @param alloc_name Name for this allocation command. + * @param origin Origin of the allocation command (used for id). + */ +void *ucs_memtrack_mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset UCS_MEMTRACK_ARG); + +/** + * Memory mapping request (for 64 bit). + * + * @param addr Address hint for mapping. + * @param length Size of the requested allocation. + * @param prot Memory protection mask. + * @param flags Other flags. + * @param fd File to map. + * @param offset Offset within the file. + * @param alloc_name Name for this allocation command. + * @param origin Origin of the allocation command (used for id). + */ +#ifdef __USE_LARGEFILE64 +void *ucs_memtrack_mmap64(void *addr, size_t length, int prot, int flags, + int fd, uint64_t offset UCS_MEMTRACK_ARG); +#endif + +/** + * Memory unmaping request. + * + * @param addr Address to unmap. + * @param length Size of the requested allocation. + */ +int ucs_memtrack_munmap(void *addr, size_t length); + +#endif diff --git a/src/ucs/gtest/main.cc b/src/ucs/gtest/main.cc new file mode 100644 index 00000000000..44bed3b9079 --- /dev/null +++ b/src/ucs/gtest/main.cc @@ -0,0 +1,55 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +#include +extern "C" { +#include +} + +class valgrind_errors_Test : public ::testing::Test, boost::noncopyable { +private: + virtual void TestBody() { + long leaked, dubious, reachable, suppressed, errors; + errors = VALGRIND_COUNT_ERRORS; + VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed); + EXPECT_EQ(0, errors); + EXPECT_EQ(0, leaked); + (void)dubious; + (void)reachable; + (void)suppressed; + } +}; + +void parse_test_opts(int argc, char **argv) { + int c; + while ((c = getopt(argc, argv, "s:")) != -1) { + switch (c) { + case 's': + srand(atoi(optarg)); + break; + default: + fprintf(stderr, "Usage: gtest [ -s rand-seed ]\n"); + exit(1); + } + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (RUNNING_ON_VALGRIND) { + ::testing::internal::MakeAndRegisterTestInfo( + "valgrind", "errors", "", "", + (::testing::internal::GetTestTypeId()), + ::testing::Test::SetUpTestCase, + ::testing::Test::TearDownTestCase, + new ::testing::internal::TestFactoryImpl); + } + parse_test_opts(argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/ucs/gtest/test.cc b/src/ucs/gtest/test.cc new file mode 100644 index 00000000000..3e463a04101 --- /dev/null +++ b/src/ucs/gtest/test.cc @@ -0,0 +1,111 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "test.h" + +extern "C" { +#include +#include +} + +#include +#include + +namespace ucs { + +test_base::test_base() : m_state(NEW) { +} + +test_base::~test_base() { + ucs_assertv_always(m_state == FINISHED || + m_state == SKIPPED || + m_state == ABORTED, + "state=%d", m_state); +} + +void test_base::set_config(void *opts, ucs_config_field_t *fields, + const std::string& name, const std::string& value) +{ + ucs_status_t status = ucs_config_parser_set_value(opts, fields, name.c_str(), + value.c_str()); + if (status != UCS_OK) { + GTEST_FAIL() << "Invalid UCS configuration for " << name << " : " << value; + } +} + +void test_base::set_config(const std::string& name, const std::string& value) +{ + set_config(&ucs_global_opts, ucs_global_opts_table, name, value); +} + +void test_base::push_config() +{ + m_config_stack.push_back(ucs_global_opts_t()); + ucs_config_parser_clone_opts(&ucs_global_opts, &m_config_stack.back(), + ucs_global_opts_table); +} + +void test_base::pop_config() +{ + ucs_config_parser_release_opts(&ucs_global_opts, ucs_global_opts_table); + ucs_global_opts = m_config_stack.back(); +} + +void test_base::set_multi_config(const std::string& multi_config) { + std::vector tokens; + boost::split(tokens, multi_config, boost::is_any_of("=")); + set_config(tokens[0], tokens[1]); +} + +void test_base::SetUpProxy() { + ucs_assert(m_state == NEW); + m_num_valgrind_errors_before = VALGRIND_COUNT_ERRORS; + + try { + init(); + m_state = RUNNING; + } catch (test_skip_exception&) { + detail::message_stream("SKIP"); + m_state = SKIPPED; + } catch (test_abort_exception&) { + m_state = ABORTED; + } +} + +void test_base::TearDownProxy() { + ucs_assertv_always(m_state == FINISHED || + m_state == SKIPPED || + m_state == ABORTED, + "state=%d", m_state); + cleanup(); + int num_valgrind_errors = VALGRIND_COUNT_ERRORS - m_num_valgrind_errors_before; + if (num_valgrind_errors > 0) { + ADD_FAILURE() << "Got " << num_valgrind_errors << " valgrind errors during the test"; + } +} + +void test_base::TestBodyProxy() { + if (m_state == RUNNING) { + try { + test_body(); + m_state = FINISHED; + } catch (test_skip_exception&) { + detail::message_stream("SKIP"); + m_state = SKIPPED; + } + } else if (m_state == SKIPPED) { + } else if (m_state == ABORTED) { + } +} + +void test_base::init() { +} + +void test_base::cleanup() { +} + +} diff --git a/src/ucs/gtest/test.h b/src/ucs/gtest/test.h new file mode 100644 index 00000000000..6e7a5fe56ce --- /dev/null +++ b/src/ucs/gtest/test.h @@ -0,0 +1,158 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_TEST_BASE_H +#define UCS_TEST_BASE_H + +#include "test_helpers.h" + +extern "C" { +#include +#include +#include +#include +} + +#include +#include +#include + +namespace ucs { + +/** + * Base class for tests + */ +class test_base { +protected: + test_base(); + virtual ~test_base(); + + virtual void set_config(const std::string& name, const std::string& value); + virtual void push_config(); + virtual void pop_config(); + + /* Helpers */ + void set_multi_config(const std::string& multi_config); + void set_config(void *opts, ucs_config_field_t *fields, + const std::string& name, const std::string& value); + +protected: + + typedef enum { + NEW, RUNNING, SKIPPED, ABORTED, FINISHED + } state_t; + + typedef std::vector config_stack_t; + + void SetUpProxy(); + void TearDownProxy(); + void TestBodyProxy(); + + virtual void cleanup(); + virtual void init(); + + virtual void test_body() = 0; + + config_stack_t m_config_stack; + state_t m_state; + bool m_skip; + int m_num_valgrind_errors_before; +}; + +#define UCS_TEST_BASE_IMPL \ + virtual void SetUp() { \ + test_base::SetUpProxy(); \ + } \ + \ + virtual void TearDown() { \ + test_base::TearDownProxy(); \ + } \ + virtual void TestBody() { \ + test_base::TestBodyProxy(); \ + } + +/* + * Base class from generic tests + */ +class test : public testing::Test, public test_base { +public: + UCS_TEST_BASE_IMPL; +}; + +} + +#define UCS_TEST_SET_CONFIG(_dummy, _config) \ + set_config(_config); + +/* + * Helper macro + */ +#define UCS_TEST_(test_case_name, test_name, parent_class, parent_id, ...)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {\ + UCS_PP_FOREACH(UCS_TEST_SET_CONFIG, _, __VA_ARGS__) \ + } \ + private:\ + virtual void test_body();\ + static ::testing::TestInfo* const test_info_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, "", "", \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::test_body() + + +/* + * Define test fixture with modified configuration + */ +#define UCS_TEST_F(test_fixture, test_name, ...)\ + UCS_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId(), __VA_ARGS__) + + +/* + * Define parameterized test with modified configuration + */ +#define UCS_TEST_P(test_case_name, test_name, ...) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {\ + UCS_PP_FOREACH(UCS_API_TEST_SET_CONFIG, _, __VA_ARGS__) \ + } \ + virtual void test_body(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::test_body() + +#endif diff --git a/src/ucs/gtest/test_helpers.cc b/src/ucs/gtest/test_helpers.cc new file mode 100644 index 00000000000..6512832781f --- /dev/null +++ b/src/ucs/gtest/test_helpers.cc @@ -0,0 +1,75 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "test_helpers.h" + +extern "C" { +#include +} + +namespace ucs { + +int test_time_multiplier() +{ + int factor = 1; +#if _BullseyeCoverage + factor *= 10; +#endif + if (RUNNING_ON_VALGRIND) { + factor *= 20; + } + return factor; +} + +std::ostream& operator<<(std::ostream& os, const std::vector& vec) { + static const size_t LIMIT = 100; + size_t i = 0; + BOOST_FOREACH(const char&value, vec) { + if (i >= LIMIT) { + os << "..."; + break; + } + int n = static_cast(value); + os << "[" << i << "]=" << n << " "; + ++i; + } + return os << std::endl; +} + +scoped_setenv::scoped_setenv(const char *name, const char *value) : m_name(name) { + if (getenv(name)) { + m_old_value.reset(getenv(m_name.c_str())); + } + setenv(m_name.c_str(), value, 1); +} + +scoped_setenv::~scoped_setenv() { + if (m_old_value) { + setenv(m_name.c_str(), m_old_value->c_str(), 1); + } else { + unsetenv(m_name.c_str()); + } +} + +namespace detail { + +message_stream::message_stream(const std::string& title) { + static const char PADDING[] = " "; + static const size_t WIDTH = strlen(PADDING); + + std::cout << "["; + std::cout.write(PADDING, ucs_max(WIDTH - 1, title.length()) - title.length()); + std::cout << title << " ] "; +} + +message_stream::~message_stream() { + std::cout << std::endl; +} + +} // detail + +} // ucs diff --git a/src/ucs/gtest/test_helpers.h b/src/ucs/gtest/test_helpers.h new file mode 100644 index 00000000000..ba094067145 --- /dev/null +++ b/src/ucs/gtest/test_helpers.h @@ -0,0 +1,158 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_TEST_HELPERS_H +#define UCS_TEST_HELPERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ucs { + +class test_abort_exception : public std::exception { +}; + + +class test_skip_exception : public std::exception { +}; + +/** + * @return Time multiplier for performance tests. + */ +int test_time_multiplier(); + +/* + * For gtest's EXPECT_EQ + */ +template +static std::ostream& operator<<(std::ostream& os, const std::vector& vec) { + static const size_t LIMIT = 2000; + size_t i = 0; + BOOST_FOREACH(const T&value, vec) { + if (i >= LIMIT) { + os << "..."; + break; + } + os << "[" << i << "]=" << value << " "; + ++i; + } + return os << std::endl; +} + +std::ostream& operator<<(std::ostream& os, const std::vector& vec); + +template +static void fill_random(OutputIterator begin, OutputIterator end) { + for (OutputIterator iter = begin; iter != end; ++iter) { + *iter = rand(); + } +} + +template +static inline T random_upper() { + return static_cast((rand() / static_cast(RAND_MAX)) * std::numeric_limits::max()); +} + +template +class hex_num { +public: + hex_num(const T num) : m_num(num) { + } + + operator T() const { + return m_num; + } + + template + friend std::ostream& operator<<(std::ostream& os, const hex_num& h); +private: + const T m_num; +}; + +template +hex_num make_hex(const T num) { + return hex_num(num); +} + +template +std::ostream& operator<<(std::ostream& os, const hex_num& h) { + return os << std::hex << h.m_num << std::dec; +} +class scoped_setenv { +public: + scoped_setenv(const char *name, const char *value); + ~scoped_setenv(); +private: + const std::string m_name; + boost::optional m_old_value; +}; + + +namespace detail { + +class message_stream { +public: + message_stream(const std::string& title); + ~message_stream(); + + template + std::ostream& operator<<(const T& value) const { + return std::cout << value; + } +}; + +} // detail + +} // ucs + + +/* Test output */ +#define UCS_TEST_MESSAGE \ + ucs::detail::message_stream("INFO") + + +/* Skip test */ +#define UCS_TEST_SKIP \ + do { \ + throw ucs::test_skip_exception(); \ + } while(0) + + +/* UCS error check */ +#define EXPECT_UCS_OK(_error) EXPECT_EQ(UCS_OK, _error) << "Error: " << ucs_error_string(_error) +#define ASSERT_UCS_OK(_error) \ + do { \ + if ((_error) != UCS_OK) { \ + std::stringstream ss; \ + ss << "Error: " << ucs_status_string(_error); \ + GTEST_MESSAGE_(ss.str().c_str(), ::testing::TestPartResult::kFatalFailure); \ + throw ucs::test_abort_exception(); \ + } \ + } while (0) + + +/* Run code block with given time limit */ +#define UCS_TEST_TIME_LIMIT(_seconds) \ + for (ucs_time_t _start_time = ucs_get_time(), _elapsed = 0; \ + _start_time != 0; \ + (ucs_time_to_sec(_elapsed = ucs_get_time() - _start_time) >= \ + (_seconds) * ucs::test_time_multiplier()) \ + ? (GTEST_NONFATAL_FAILURE_("Time limit exceeded:") << \ + "Expected time: " << ((_seconds) * ucs::test_time_multiplier()) << " seconds\n" << \ + "Actual time: " << ucs_time_to_sec(_elapsed) << " seconds", 0) \ + : 0, \ + _start_time = 0) + + +#endif diff --git a/src/ucs/stats/client_server.c b/src/ucs/stats/client_server.c new file mode 100644 index 00000000000..ae4a35435b8 --- /dev/null +++ b/src/ucs/stats/client_server.c @@ -0,0 +1,654 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "libstats.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define UCS_STATS_MAGIC "UCSSTAT1" +#define UCS_STATS_MSG_FRAG_SIZE 1400 +#define ENTITY_HASH_SIZE 997 + + +/* UDP packet header */ +typedef struct ucs_stats_packet_hdr { + char magic[8]; + uint64_t timestamp; + uint32_t total_size; + uint32_t frag_offset; + uint32_t frag_size; +} UCS_S_PACKED ucs_stats_packet_hdr_t; + + +/* Fragment assembly hole free-list */ +typedef struct frag_hole { + ucs_list_link_t list; + size_t size; /* Including this struct */ +} frag_hole_t; + + +/* An entity which reports statistics */ +typedef struct stats_entity stats_entity_t; +struct stats_entity { + struct sockaddr_in in_addr; /* Entity address */ + uint64_t timestamp; /* Current timestamp */ + size_t buffer_size; /* Buffer size */ + void *inprogress_buffer; /* Fragment assembly buffer */ + ucs_list_link_t holes; /* List of holes in the buffer */ + stats_entity_t *next; /* Hash link */ + + pthread_mutex_t lock; + volatile unsigned refcount; + void *completed_buffer; /* Completed buffer */ + struct timeval update_time; +}; + + +/* Client context */ +typedef struct ucs_stats_client { + int sockfd; +} ucs_stats_client_t; + + +/* Server context */ +typedef struct ucs_stats_server { + int sockfd; + int udp_port; + pthread_t server_thread; + int stop; + ucs_list_link_t curr_stats; + pthread_mutex_t entities_lock; + stats_entity_t* entities_hash[ENTITY_HASH_SIZE]; +} ucs_stats_server_t; + + +SGLIB_DEFINE_LIST_PROTOTYPES(stats_entity_t, stats_entity_cmp, next) +SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(stats_entity_t, ENTITY_HASH_SIZE, stats_entity_hash) + + +ucs_status_t ucs_stats_client_init(const char *server_addr, int port, ucs_stats_client_h *p_client) +{ + ucs_stats_client_h client; + struct sockaddr_in saddr; + struct hostent *he; + ucs_status_t status; + int ret; + + client = malloc(sizeof *client); + if (client == NULL) { + status = UCS_ERR_NO_MEMORY; + goto err; + } + + he = gethostbyname(server_addr); + if (he == NULL || he->h_addr_list == NULL) { + ucs_error("failed to resolve address of '%s'", server_addr); + status = UCS_ERR_INVALID_ADDR; + goto err_free; + } + + saddr.sin_family = he->h_addrtype; + saddr.sin_port = htons(port); + assert(he->h_length == sizeof(saddr.sin_addr)); + memcpy(&saddr.sin_addr, he->h_addr_list[0], he->h_length); + memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero)); + + client->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (client->sockfd < 0) { + ucs_error("socket() failed: %m"); + status = UCS_ERR_IO_ERROR; + goto err_free; + } + + ret = connect(client->sockfd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + ucs_error("connect(%d) failed: %m", client->sockfd); + status = UCS_ERR_IO_ERROR; + goto err_close; + } + + *p_client = client; + return UCS_OK; + +err_close: + close(client->sockfd); +err_free: + free(client); +err: + return status; +} + +void ucs_stats_client_cleanup(ucs_stats_client_h client) +{ + close(client->sockfd); + free(client); +} + +static ucs_status_t +ucs_stats_sock_send_frags(int sockfd, uint64_t timestamp, void *buffer, size_t size) +{ + struct iovec iov[2]; + ucs_stats_packet_hdr_t hdr; + size_t frag_size, offset, nsent; + size_t max_frag = UCS_STATS_MSG_FRAG_SIZE - sizeof(hdr); + + offset = 0; + + memcpy(hdr.magic, UCS_STATS_MAGIC, sizeof(hdr.magic)); + hdr.total_size = size; + hdr.timestamp = timestamp; + + while (offset < size) { + frag_size = size - offset; + if (frag_size > max_frag) { + frag_size = max_frag; + } + + hdr.frag_offset = offset; + hdr.frag_size = frag_size; + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + iov[1].iov_base = buffer + offset; + iov[1].iov_len = hdr.frag_size; + + nsent = writev(sockfd, iov, 2); + if (nsent == -1) { + if (errno == ECONNREFUSED) { + ucs_trace("stats server is down"); + return UCS_OK; + } else { + ucs_error("writev() failed: %m"); + return UCS_ERR_IO_ERROR; + } + } + + assert(nsent == sizeof(hdr) + frag_size); + offset += frag_size; + } + + return UCS_OK; +} + +ucs_status_t +ucs_stats_client_send(ucs_stats_client_h client, ucs_stats_node_t *root, + uint64_t timestamp) +{ + ucs_status_t status; + FILE *stream; + char *buffer; + size_t size; + + /* TODO use GLIBC custom stream */ + stream = open_memstream(&buffer, &size); + if (stream == NULL) { + status = UCS_ERR_NO_MEMORY; + goto out; + } + + status = ucs_stats_serialize(stream, root, UCS_STATS_SERIALIZE_BINARY); + fclose(stream); + + if (status != UCS_OK) { + goto out_free; + } + + /* send */ + status = ucs_stats_sock_send_frags(client->sockfd, timestamp, buffer, size); + +out_free: + free(buffer); +out: + return status; +} + +static void ucs_stats_server_entity_reset_buffer(stats_entity_t * entity, + size_t new_size) +{ + frag_hole_t *hole; + + if (new_size != entity->buffer_size) { + pthread_mutex_lock(&entity->lock); + entity->buffer_size = new_size; + entity->inprogress_buffer = realloc(entity->inprogress_buffer, + new_size + sizeof(frag_hole_t)); + entity->completed_buffer = realloc(entity->completed_buffer, + new_size + sizeof(frag_hole_t)); + pthread_mutex_unlock(&entity->lock); + } + + hole = entity->inprogress_buffer; + hole->size = entity->buffer_size; + ucs_list_head_init(&entity->holes); + ucs_list_add_tail(&entity->holes, &hole->list); +} + +static stats_entity_t *ucs_stats_server_entity_alloc(struct sockaddr_in *addr) +{ + stats_entity_t *entity; + + entity = malloc(sizeof *entity); + if (entity == NULL) { + return NULL; + } + + entity->in_addr = *addr; + entity->timestamp = 0; + entity->buffer_size = -1; + entity->inprogress_buffer = NULL; + entity->completed_buffer = NULL; + entity->refcount = 1; + ucs_list_head_init(&entity->holes); + pthread_mutex_init(&entity->lock, NULL); + + ucs_stats_server_entity_reset_buffer(entity, 0); + return entity; +} + +static void ucs_stats_server_entity_free(stats_entity_t * entity) +{ + free(entity->inprogress_buffer); + free(entity->completed_buffer); + free(entity); +} + +static stats_entity_t* +ucs_stats_server_entity_get(ucs_stats_server_h server, struct sockaddr_in *addr) +{ + stats_entity_t *entity, search; + + pthread_mutex_lock(&server->entities_lock); + search.in_addr = *addr; + + entity = sglib_hashed_stats_entity_t_find_member(server->entities_hash, &search); + if (entity == NULL) { + entity = ucs_stats_server_entity_alloc(addr); + gettimeofday(&entity->update_time, NULL); + sglib_hashed_stats_entity_t_add(server->entities_hash, entity); + } + + __sync_fetch_and_add(&entity->refcount, 1); + pthread_mutex_unlock(&server->entities_lock); + + return entity; +} + +static void ucs_stats_server_entity_put(stats_entity_t * entity) +{ + if (__sync_fetch_and_add(&entity->refcount, -1) == 1) { + ucs_stats_server_entity_free(entity); + } +} + +/** + * Find a hole to contain the given fragment. + */ +static frag_hole_t * +find_frag_hole(stats_entity_t *entity, size_t frag_size, size_t frag_offset) +{ + void *frag_start = entity->inprogress_buffer + frag_offset; + void *frag_end = entity->inprogress_buffer + frag_offset + frag_size; + frag_hole_t *hole; + + ucs_list_for_each(hole, &entity->holes, list) { + if ((frag_start >= (void*)hole) && (frag_end <= (void*)hole + hole->size)) { + return hole; + } + } + return NULL; +} + +/** + * Update statistics with new arrived fragment. + */ +static ucs_status_t +ucs_stats_server_entity_update(ucs_stats_server_h server, stats_entity_t *entity, + uint64_t timestamp, size_t total_size, void *frag, + size_t frag_size, size_t frag_offset) +{ + frag_hole_t *hole, *new_hole; + void *frag_start, *frag_end, *hole_end; + + ucs_debug("From %s:%d - timestamp %"PRIu64", %Zu..%Zu / %Zu", + inet_ntoa(entity->in_addr.sin_addr), ntohs(entity->in_addr.sin_port), + timestamp, frag_offset, frag_offset + frag_size, total_size); + + if (timestamp < entity->timestamp) { + ucs_debug("Dropping - old timestamp"); + return 0; + } else if (timestamp > entity->timestamp) { + ucs_debug("New timestamp, resetting buffer with size %Zu", total_size); + entity->timestamp = timestamp; + ucs_stats_server_entity_reset_buffer(entity, total_size); + } else { + /* Make sure all packets in this timestamp have the same 'total_size' */ + if (entity->buffer_size != total_size) { + ucs_error("Total size in the packet is %Zu, but expected is %Zu", + total_size, entity->buffer_size); + } + } + + hole = find_frag_hole(entity, frag_size, frag_offset); + if (hole == NULL) { + ucs_error("cannot fill fragment (offset %Zu size %Zu)", frag_offset, frag_size); + return UCS_ERR_MESSAGE_TRUNCATED; + } + + frag_start = entity->inprogress_buffer + frag_offset; + frag_end = entity->inprogress_buffer + frag_offset + frag_size; + hole_end = (void*)hole + hole->size; + + ucs_debug("inserting into a hole of %Zu..%Zu", + (void*)hole - entity->inprogress_buffer, + hole_end - entity->inprogress_buffer); + + /* If the fragment does not reach the end of the hole, create a new hole + * in this space. + */ + if (frag_end < hole_end) { + /* Make sure we don't create a hole which is too small for a free-list + * pointer to fit in. An exception is the last fragment. + */ + assert((hole_end - frag_end >= sizeof(*new_hole)) || + (hole_end == entity->inprogress_buffer + entity->buffer_size)); + new_hole = frag_end; + new_hole->size = hole_end - frag_end; + ucs_list_insert_after(&hole->list, &new_hole->list); + } + + /* If we have room before the fragment, resize the hole. Otherwise, delete it */ + if (frag_start > (void*)hole) { + assert(frag_start - (void*)hole >= sizeof(*hole)); + hole->size = frag_start - (void*)hole; + } else { + ucs_list_del(&hole->list); + } + + /* Copy the fragment */ + memcpy(frag_start, frag, frag_size); + + /* Completed? */ + if (ucs_list_is_empty(&entity->holes)) { + ucs_debug("timestamp %"PRIu64" fully assembled", entity->timestamp); + pthread_mutex_lock(&entity->lock); + memcpy(entity->completed_buffer, entity->inprogress_buffer, entity->buffer_size); + pthread_mutex_unlock(&entity->lock); + } + + return UCS_OK; +} + +/** + * Update context with new arrived packet. + */ +static ucs_status_t +ucs_stats_server_update_context(ucs_stats_server_h server, struct sockaddr_in *sender, + ucs_stats_packet_hdr_t *pkt, size_t pkt_len) +{ + stats_entity_t *entity; + ucs_status_t status; + + /* Validate fragment size */ + if (pkt_len != pkt->frag_size + sizeof(ucs_stats_packet_hdr_t)) { + ucs_error("Invalid receive size: expected %Zu, got %Zu", + pkt->frag_size + sizeof(ucs_stats_packet_hdr_t), pkt_len); + return UCS_ERR_MESSAGE_TRUNCATED; + } + + /* Validate magic */ + if (memcmp(pkt->magic, UCS_STATS_MAGIC, sizeof(pkt->magic)) != 0) { + ucs_error("Invalid magic in packet header"); + return UCS_ERR_INVALID_PARAM; + } + + /* Find or create the entity */ + entity = ucs_stats_server_entity_get(server, sender); + + pthread_mutex_lock(&entity->lock); + gettimeofday(&entity->update_time, NULL); + pthread_mutex_unlock(&entity->lock); + + /* Update the entity */ + status = ucs_stats_server_entity_update(server, entity, pkt->timestamp, + pkt->total_size, pkt + 1, + pkt->frag_size, pkt->frag_offset); + + ucs_stats_server_entity_put(entity); + return status; +} + +static ucs_status_t ucs_stats_server_create_socket(int udp_port, int *p_sockfd, + int *p_udp_port) +{ + struct sockaddr_in saddr; + socklen_t socklen; + int sockfd; + int ret; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sockfd < 0) { + ucs_error("socked() failed: %m"); + return UCS_ERR_IO_ERROR; + } + + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = INADDR_ANY; + saddr.sin_port = htons(udp_port); + memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero)); + + ret = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); + if (ret < 0) { + ucs_error("Failed to bind socket to port %u: %m", udp_port); + close(sockfd); + return UCS_ERR_INVALID_ADDR; + } + + socklen = sizeof(saddr); + ret = getsockname(sockfd, (struct sockaddr*)&saddr, &socklen); + if (ret < 0) { + ucs_error("getsockname(%d) failed: %m", sockfd); + close(sockfd); + return UCS_ERR_IO_ERROR; + } + + *p_sockfd = sockfd; + *p_udp_port = ntohs(saddr.sin_port); + return UCS_OK; +} + +static void ucs_stats_server_clear_old_enitities(ucs_stats_server_h server) +{ + struct sglib_hashed_stats_entity_t_iterator it; + stats_entity_t *entity; + struct timeval current, diff; + + gettimeofday(¤t, NULL); + + pthread_mutex_lock(&server->entities_lock); + entity = sglib_hashed_stats_entity_t_it_init(&it,server->entities_hash); + while (entity != NULL) { + pthread_mutex_lock(&entity->lock); + timersub(¤t, &entity->update_time, &diff); + pthread_mutex_unlock(&entity->lock); + + if (diff.tv_sec > 5.0) { + sglib_hashed_stats_entity_t_delete(server->entities_hash, entity); + ucs_stats_server_entity_put(entity); + } + entity = sglib_hashed_stats_entity_t_it_next(&it); + } + + pthread_mutex_unlock(&server->entities_lock); +} + +static void* ucs_stats_server_thread_func(void *arg) +{ + ucs_stats_server_h server = arg; + struct sockaddr_in recv_addr; + socklen_t recv_addr_len; + char recv_buf[UCS_STATS_MSG_FRAG_SIZE]; + ssize_t recv_len; + ucs_status_t status; + + ucs_debug("starting server thread"); + while (!server->stop) { + recv_addr_len = sizeof(recv_addr); + recv_len = recvfrom(server->sockfd, recv_buf, UCS_STATS_MSG_FRAG_SIZE, 0, + (struct sockaddr*)&recv_addr, &recv_addr_len); + if (recv_len < 0) { + ucs_error("recvfrom() failed: %m (return value: %ld)", recv_len); + break; + } else if (recv_len == 0) { + ucs_debug("Empty receive - ignoring"); + continue; + } + + if (recv_addr.sin_family != AF_INET) { + ucs_error("invalid address family from recvfrom()"); + break; + } + + /* Update with new data */ + status = ucs_stats_server_update_context(server, &recv_addr, (void*)recv_buf, recv_len); + if (status != UCS_OK) { + break; + } + + ucs_stats_server_clear_old_enitities(server); + } + + ucs_debug("terminating server thread"); + return NULL; +} + +ucs_status_t ucs_stats_server_start(int port, ucs_stats_server_h *p_server) +{ + ucs_stats_server_h server; + ucs_status_t status; + + server = malloc(sizeof *server); + if (server == NULL) { + ucs_error("Failed to allocate stats context"); + return UCS_ERR_NO_MEMORY; + } + + pthread_mutex_init(&server->entities_lock, NULL); + ucs_list_head_init(&server->curr_stats); + sglib_hashed_stats_entity_t_init(server->entities_hash); + + status = ucs_stats_server_create_socket(port, &server->sockfd, &server->udp_port); + if (status != UCS_OK) { + free(server); + return status; + } + + server->stop = 0; + pthread_create(&server->server_thread, NULL, ucs_stats_server_thread_func, + server); + + *p_server = server; + return UCS_OK; +} + +void ucs_stats_server_destroy(ucs_stats_server_h server) +{ + struct sglib_hashed_stats_entity_t_iterator it; + stats_entity_t *entity; + void *retval; + + server->stop = 1; + shutdown(server->sockfd, SHUT_RDWR); + pthread_join(server->server_thread, &retval); + close(server->sockfd); + + ucs_stats_server_purge_stats(server); + + entity = sglib_hashed_stats_entity_t_it_init(&it,server->entities_hash); + while (entity != NULL) { + ucs_stats_server_entity_put(entity); + entity = sglib_hashed_stats_entity_t_it_next(&it); + } + free(server); +} + +int ucs_stats_server_get_port(ucs_stats_server_h server) +{ + return server->udp_port; +} + +ucs_list_link_t *ucs_stats_server_get_stats(ucs_stats_server_h server) +{ + struct sglib_hashed_stats_entity_t_iterator it; + stats_entity_t *entity; + ucs_stats_node_t *node; + ucs_status_t status; + FILE *stream; + + ucs_stats_server_purge_stats(server); + + pthread_mutex_lock(&server->entities_lock); + for (entity = sglib_hashed_stats_entity_t_it_init(&it, server->entities_hash); + entity != NULL; entity = sglib_hashed_stats_entity_t_it_next(&it)) + { + /* Parse the statistics data */ + pthread_mutex_lock(&entity->lock); + stream = fmemopen(entity->completed_buffer, entity->buffer_size, "rb"); + status = ucs_stats_deserialize(stream, &node); + fclose(stream); + pthread_mutex_unlock(&entity->lock); + + if (status == UCS_OK) { + ucs_list_add_tail(&server->curr_stats, &node->list); + } + } + pthread_mutex_unlock(&server->entities_lock); + + return &server->curr_stats; +} + +void ucs_stats_server_purge_stats(ucs_stats_server_h server) +{ + ucs_stats_node_t *node, *tmp; + + ucs_list_for_each_safe(node, tmp, &server->curr_stats, list) { + ucs_list_del(&node->list); + ucs_stats_free(node); + } +} + +static inline int stats_entity_cmp(stats_entity_t *e1, stats_entity_t *e2) +{ + int addr_diff = e1->in_addr.sin_addr.s_addr < e2->in_addr.sin_addr.s_addr; + if (addr_diff != 0) { + return addr_diff; + } else { + return ntohs(e1->in_addr.sin_port) - ntohs(e1->in_addr.sin_port); + } +} + +static inline int stats_entity_hash(stats_entity_t *e) +{ + return (((uint64_t)e->in_addr.sin_addr.s_addr << 16) + (uint64_t)ntohs(e->in_addr.sin_port)) % ENTITY_HASH_SIZE; +} + +SGLIB_DEFINE_LIST_FUNCTIONS(stats_entity_t, stats_entity_cmp, next) +SGLIB_DEFINE_HASHED_CONTAINER_FUNCTIONS(stats_entity_t, ENTITY_HASH_SIZE, stats_entity_hash) diff --git a/src/ucs/stats/libstats.c b/src/ucs/stats/libstats.c new file mode 100644 index 00000000000..f3cad5ef854 --- /dev/null +++ b/src/ucs/stats/libstats.c @@ -0,0 +1,66 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "libstats.h" + +#include +#include +#include + + +#define UCS_STATS_NAME_VALID_CHARS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" + + +static ucs_status_t ucs_stats_name_check(const char *name) +{ + size_t length, valid_length; + + length = strlen(name); + if (length > UCS_STAT_NAME_MAX) { + ucs_error("stats name '%s' is too long (%Zu)", name, length); + return UCS_ERR_INVALID_PARAM; + } + + valid_length = strspn(name, UCS_STATS_NAME_VALID_CHARS); + if (valid_length != length) { + ucs_error("stats name '%s' contains invalid character at offset %Zu", + name, valid_length); + return UCS_ERR_INVALID_PARAM; + } + + return UCS_OK;; +} + +ucs_status_t ucs_stats_node_initv(ucs_stats_node_t *node, ucs_stats_class_t *cls, + const char *name, va_list ap) +{ + ucs_status_t status; + unsigned i; + + /* Check class */ + status = ucs_stats_name_check(cls->name); + if (status != UCS_OK) { + return status; + } + for (i = 0; i < cls->num_counters; ++i) { + status = ucs_stats_name_check(cls->counter_names[i]); + if (status != UCS_OK) { + return status; + } + } + + /* Set up node */ + node->cls = cls; + vsnprintf(node->name, UCS_STAT_NAME_MAX, name, ap); + ucs_list_head_init(&node->children[UCS_STATS_INACTIVE_CHILDREN]); + ucs_list_head_init(&node->children[UCS_STATS_ACTIVE_CHILDREN]); + memset(node->counters, 0, cls->num_counters * sizeof(ucs_stats_counter_t)); + + return UCS_OK; +} + diff --git a/src/ucs/stats/libstats.h b/src/ucs/stats/libstats.h new file mode 100644 index 00000000000..17aeb2e620c --- /dev/null +++ b/src/ucs/stats/libstats.h @@ -0,0 +1,185 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_LIBSTATS_H_ +#define UCS_LIBSTATS_H_ + +#include +#include +#include + +#include +#include + + +/* + * Serialization options + */ +enum { + UCS_STATS_SERIALIZE_INACTVIVE = UCS_BIT(0), /* Use "inactive" tree */ + UCS_STATS_SERIALIZE_BINARY = UCS_BIT(1), /* Binary mode */ + UCS_STATS_SERIALIZE_COMPRESS = UCS_BIT(2) /* Compress */ +}; + +#define UCS_STATS_DEFAULT_UDP_PORT 37873 + + +#define UCS_STAT_NAME_MAX 31 + +#define UCS_STATS_NODE_FMT \ + "%s%s" +#define UCS_STATS_NODE_ARG(_node) \ + (_node)->cls->name, (_node)->name + + +typedef uint64_t ucs_stats_counter_t; /* Stats counter*/ +typedef struct ucs_stats_class ucs_stats_class_t; /* Stats class */ +typedef struct ucs_stats_node ucs_stats_node_t; /* Stats node */ +typedef struct ucs_stats_server *ucs_stats_server_h; /* Handle to server */ +typedef struct ucs_stats_client *ucs_stats_client_h; /* Handle to client */ + + +typedef enum ucs_stats_children_sel { + UCS_STATS_INACTIVE_CHILDREN, + UCS_STATS_ACTIVE_CHILDREN, + UCS_STATS_CHILDREN_LAST +} ucs_stats_children_sel_t; + + +/* Statistics class */ +struct ucs_stats_class { + const char *name; + unsigned num_counters; + const char* counter_names[]; +}; + +/* In-memory statistics node */ +struct ucs_stats_node { + ucs_stats_class_t *cls; + ucs_stats_node_t *parent; + char name[UCS_STAT_NAME_MAX + 1]; + ucs_list_link_t list; + ucs_list_link_t children[UCS_STATS_CHILDREN_LAST]; + ucs_stats_counter_t counters[]; +}; + + +/** + * Initialize statistics node. + * + * @param node Node to initialize. + * @param cls Node class. + * @param name Node name format string. + * @param ap Name formatting arguments. + */ +ucs_status_t ucs_stats_node_initv(ucs_stats_node_t *node, ucs_stats_class_t *cls, + const char *name, va_list ap); + + +/** + * Serialize statistics. + * + * @param stream Destination + * @param root Statistics node root. + * @param options Serialization options. + */ +ucs_status_t ucs_stats_serialize(FILE *stream, ucs_stats_node_t *root, int options); + + +/** + * De-serialize statistics. + * + * @param stream Source data. + * @param p_roo Filled with tatistics node root. + * + * @return UCS_ERR_NO_ELEM if hit EOF. + */ +ucs_status_t ucs_stats_deserialize(FILE *stream, ucs_stats_node_t **p_root); + + +/** + * Release stats returned by ucs_stats_deserialize(). + * @param root Stats to release. + */ +void ucs_stats_free(ucs_stats_node_t *root); + + +/** + * Initialize statistics client. + * + * @param server_addr Address of server machine. + * @param port Port number on server. + * @param p_client Filled with handle to the client. + */ +ucs_status_t ucs_stats_client_init(const char *server_addr, int port, + ucs_stats_client_h *p_client); + + +/** + * Destroy statistics client. + */ +void ucs_stats_client_cleanup(ucs_stats_client_h client); + + +/** + * Send statistics. + * + * @param client Client handle. + * @param root Statistics tree root. + * @param timestamp Current statistics timestamp, identifies every "snapshot". + */ +ucs_status_t ucs_stats_client_send(ucs_stats_client_h client, ucs_stats_node_t *root, + uint64_t timestamp); + + +/** + * Start a thread running a server which receives statistics. + * + * @param port Port number to listen on. 0 - random available port. + * @param verbose Verbose level. + * @param p_server Filled with handle to the server. + */ +ucs_status_t ucs_stats_server_start(int port, ucs_stats_server_h *p_server); + + +/** + * Stop statistics server. + * @param server Handle to statistics server. + */ +void ucs_stats_server_destroy(ucs_stats_server_h server); + + +/** + * Get port number used by the server, useful if we started it on a random port. + * + * @param server Handle to statistics server. + * + * @return Port number. + */ +int ucs_stats_server_get_port(ucs_stats_server_h server); + + +/** + * Get current statistics gathered by the server. The data is valid until the next + * call to any of the following functions: + * - ucs_stats_server_purge_stats + * - ucs_stats_server_cleanup + * - ucs_stats_server_get_stats + * + * @param server Handle to statistics server. + * @return A list of stat trees for all entities gathered by the server. + */ +ucs_list_link_t *ucs_stats_server_get_stats(ucs_stats_server_h server); + + +/** + * Clean up existing statistics. + */ +void ucs_stats_server_purge_stats(ucs_stats_server_h server); + + +#endif /* LIBSTATS_H_ */ diff --git a/src/ucs/stats/serialization.c b/src/ucs/stats/serialization.c new file mode 100644 index 00000000000..7b0580b266e --- /dev/null +++ b/src/ucs/stats/serialization.c @@ -0,0 +1,505 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "libstats.h" + +#include +#include +#include +#include +#include +#include +#include + + +/* Class table */ +#define UCS_STATS_CLS_HASH_SIZE 127 +#define UCS_STATS_CLSID_HASH(a) ( (uintptr_t)((a)->cls) ) +#define UCS_STATS_CLSID_CMP(a, b) ( ((long)((a)->cls)) - ((long)((b)->cls)) ) +#define UCS_STATS_CLSID_SENTINEL UINT8_MAX + +/* Encode counter size */ +#define UCS_STATS_BITS_PER_COUNTER 2 +#define UCS_STATS_COUNTER_ZERO 0 +#define UCS_STATS_COUNTER_U16 1 +#define UCS_STATS_COUNTER_U32 2 +#define UCS_STATS_COUNTER_U64 3 + + +/* Compression mode */ +#define UCS_STATS_COMPRESSION_NONE 0 +#define UCS_STATS_COMPRESSION_BZIP2 1 + + +/* Statistics data header */ +typedef struct ucs_stats_data_header { + uint32_t version; + uint32_t reserved; + uint32_t compression; + uint32_t num_classes; +} ucs_stats_data_header_t; + + +/* Class id record */ +typedef struct ucs_stats_clsid ucs_stats_clsid_t; +struct ucs_stats_clsid { + uint8_t clsid; + ucs_stats_class_t *cls; + ucs_stats_clsid_t *next; +}; + + +/* Save pointer to class table near the root node */ +typedef struct ucs_stats_root_storage { + ucs_stats_class_t **classes; + unsigned num_classes; + ucs_stats_node_t node; +} ucs_stats_root_storage_t; + + +SGLIB_DEFINE_LIST_PROTOTYPES(ucs_stats_clsid_t, UCS_STATS_CLSID_CMP, next) +SGLIB_DEFINE_LIST_FUNCTIONS(ucs_stats_clsid_t, UCS_STATS_CLSID_CMP, next) +SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(ucs_stats_clsid_t, UCS_STATS_CLS_HASH_SIZE, UCS_STATS_CLSID_HASH) +SGLIB_DEFINE_HASHED_CONTAINER_FUNCTIONS(ucs_stats_clsid_t, UCS_STATS_CLS_HASH_SIZE, UCS_STATS_CLSID_HASH) + +#define FREAD(_buf, _size, _stream) \ + { \ + size_t nread = fread(_buf, 1, _size, _stream); \ + assert(nread == _size); \ + } + +#define FWRITE(_buf, _size, _stream) \ + { \ + size_t nwrite = fwrite(_buf, 1, _size, _stream); \ + assert(nwrite == _size); \ + } + +#define FREAD_ONE(_ptr, _stream) \ + FREAD(_ptr, sizeof(*(_ptr)), _stream) + +#define FWRITE_ONE(_ptr, _stream) \ + FWRITE(_ptr, sizeof(*(_ptr)), _stream) + + +static unsigned ucs_stats_get_all_classes_recurs(ucs_stats_node_t *node, + ucs_stats_children_sel_t sel, + ucs_stats_clsid_t **cls_hash) +{ + ucs_stats_clsid_t *elem, search; + ucs_stats_node_t *child; + unsigned count; + + search.cls = node->cls; + if (!sglib_hashed_ucs_stats_clsid_t_find_member(cls_hash, &search)) { + elem = malloc(sizeof *elem); + elem->cls = node->cls; + sglib_hashed_ucs_stats_clsid_t_add(cls_hash, elem); + count = 1; + } else { + count = 0; + } + + ucs_list_for_each(child, &node->children[sel], list) { + count += ucs_stats_get_all_classes_recurs(child, sel, cls_hash); + } + + return count; +} + +static char * ucs_stats_read_str(FILE *stream) +{ + uint8_t tmp; + char *str; + + FREAD_ONE(&tmp, stream); + str = malloc(tmp + 1); + FREAD(str, tmp, stream); + str[tmp] = '\0'; + return str; +} + +static void ucs_stats_write_str(const char *str, FILE *stream) +{ + uint8_t tmp = strlen(str); + + FWRITE_ONE(&tmp, stream); + FWRITE(str, tmp, stream); +} + +static void ucs_stats_read_counters(ucs_stats_counter_t *counters, + unsigned num_counters, + FILE *stream) +{ + const unsigned counters_per_byte = 8 / UCS_STATS_BITS_PER_COUNTER; + uint16_t value16; + uint32_t value32; + uint64_t value64; + uint8_t *counter_desc, v; + size_t counter_desc_size; + unsigned i; + + counter_desc_size = ((num_counters + counters_per_byte - 1) / counters_per_byte); + counter_desc = alloca(counter_desc_size); + FREAD(counter_desc, counter_desc_size, stream); + + for (i = 0; i < num_counters; ++i) { + v = (counter_desc[i / counters_per_byte] >> + ((i % counters_per_byte) * UCS_STATS_BITS_PER_COUNTER)) & 0x3; + switch (v) { + case UCS_STATS_COUNTER_ZERO: + counters[i] = 0; + break; + case UCS_STATS_COUNTER_U16: + FREAD_ONE(&value16, stream); + counters[i] = value16; + break; + case UCS_STATS_COUNTER_U32: + FREAD_ONE(&value32, stream); + counters[i] = value32; + break; + case UCS_STATS_COUNTER_U64: + FREAD_ONE(&value64, stream); + counters[i] = value64; + break; + } + } +} + +static void ucs_stats_write_counters(ucs_stats_counter_t *counters, + unsigned num_counters, + FILE *stream) +{ + const unsigned counters_per_byte = 8 / UCS_STATS_BITS_PER_COUNTER; + ucs_stats_counter_t value; + uint8_t *counter_desc, v; + void *counter_data, *pos; + size_t counter_desc_size; + unsigned i; + + UCS_STATIC_ASSERT((8 % UCS_STATS_BITS_PER_COUNTER) == 0); + counter_desc_size = ((num_counters + counters_per_byte - 1) / counters_per_byte); + counter_desc = alloca(counter_desc_size); + counter_data = alloca(num_counters * sizeof(ucs_stats_counter_t)); + + memset(counter_desc, 0, counter_desc_size); + pos = counter_data; + + /* + * First, we have an array with 2 bits per counter describing its size: + * (0 - empty, 1 - 16bit, 2 - 32bit, 3 - 64bit) + * Then, an array of all counters, each one occupying the size listed before. + */ + for (i = 0; i < num_counters; ++i) { + value = counters[i]; + if (value == 0) { + v = UCS_STATS_COUNTER_ZERO; + } else if (value <= USHRT_MAX) { + v = UCS_STATS_COUNTER_U16; + *(uint16_t*)(pos) = value; + pos += sizeof(uint16_t); + } else if (value <= UINT_MAX) { + v = UCS_STATS_COUNTER_U32; + *(uint32_t*)(pos) = value; + pos += sizeof(uint32_t); + } else { + v = UCS_STATS_COUNTER_U64; + *(uint64_t*)(pos) = value; + pos += sizeof(uint64_t); + } + counter_desc[i / counters_per_byte] |= + v << ((i % counters_per_byte) * UCS_STATS_BITS_PER_COUNTER); + } + + FWRITE(counter_desc, counter_desc_size, stream); + FWRITE(counter_data, pos - counter_data, stream); +} + +static void +ucs_stats_serialize_binary_recurs(FILE *stream, ucs_stats_node_t *node, + ucs_stats_children_sel_t sel, + ucs_stats_clsid_t **cls_hash) +{ + ucs_stats_class_t *cls = node->cls; + ucs_stats_clsid_t *elem, search; + ucs_stats_node_t *child; + uint8_t sentinel; + + /* Search the class */ + search.cls = cls; + elem = sglib_hashed_ucs_stats_clsid_t_find_member(cls_hash, &search); + assert(elem != NULL); + + /* Write class ID */ + FWRITE_ONE(&elem->clsid, stream); + + /* Name */ + ucs_stats_write_str(node->name, stream); + + /* Counters */ + ucs_stats_write_counters(node->counters, cls->num_counters, stream); + + /* Children */ + ucs_list_for_each(child, &node->children[sel], list) { + ucs_stats_serialize_binary_recurs(stream, child, sel, cls_hash); + } + + /* Write sentinel which is not valid class id to mark end of children */ + sentinel = UCS_STATS_CLSID_SENTINEL; + FWRITE_ONE(&sentinel, stream); +} + +static ucs_status_t +ucs_stats_serialize_binary(FILE *stream, ucs_stats_node_t *root, + ucs_stats_children_sel_t sel) +{ + ucs_stats_clsid_t* cls_hash[UCS_STATS_CLS_HASH_SIZE]; + struct sglib_hashed_ucs_stats_clsid_t_iterator it; + ucs_stats_class_t *cls; + ucs_stats_clsid_t *elem; + ucs_stats_data_header_t hdr; + unsigned index, counter; + + sglib_hashed_ucs_stats_clsid_t_init(cls_hash); + + /* Write header */ + hdr.version = 1; + hdr.compression = UCS_STATS_COMPRESSION_NONE; + hdr.reserved = 0; + hdr.num_classes = ucs_stats_get_all_classes_recurs(root, sel, cls_hash); + assert(hdr.num_classes < UINT8_MAX); + FWRITE_ONE(&hdr, stream); + + /* Write stats node classes */ + index = 0; + for (elem = sglib_hashed_ucs_stats_clsid_t_it_init(&it, cls_hash); + elem != NULL; elem = sglib_hashed_ucs_stats_clsid_t_it_next(&it)) + { + cls = elem->cls; + ucs_stats_write_str(cls->name, stream); + FWRITE_ONE(&cls->num_counters, stream); + for (counter = 0; counter < cls->num_counters; ++counter) { + ucs_stats_write_str(cls->counter_names[counter], stream); + } + elem->clsid = index++; + } + + assert(index == hdr.num_classes); + + /* Write stats nodes */ + ucs_stats_serialize_binary_recurs(stream, root, sel, cls_hash); + + /* Free classes */ + for (elem = sglib_hashed_ucs_stats_clsid_t_it_init(&it, cls_hash); + elem != NULL; elem = sglib_hashed_ucs_stats_clsid_t_it_next(&it)) + { + free(elem); + } + + return UCS_OK; +} + +static ucs_status_t +ucs_stats_serialize_text_recurs(FILE *stream, ucs_stats_node_t *node, + ucs_stats_children_sel_t sel, unsigned indent) +{ + ucs_stats_node_t *child; + unsigned i; + + fprintf(stream, "%*s"UCS_STATS_NODE_FMT":\n", indent * 2, "", + UCS_STATS_NODE_ARG(node)); + + for (i = 0; i < node->cls->num_counters; ++i) { + fprintf(stream, "%*s%s: %"PRIu64"\n", (indent + 1) * 2, "", + node->cls->counter_names[i], node->counters[i]); + } + + ucs_list_for_each(child, &node->children[sel], list) { + ucs_stats_serialize_text_recurs(stream, child, sel, indent + 1); + } + + return UCS_OK; +} + +ucs_status_t ucs_stats_serialize(FILE *stream, ucs_stats_node_t *root, int options) +{ + ucs_stats_children_sel_t sel = + (options & UCS_STATS_SERIALIZE_INACTVIVE) ? + UCS_STATS_INACTIVE_CHILDREN : + UCS_STATS_ACTIVE_CHILDREN; + + if (options & UCS_STATS_SERIALIZE_BINARY) { + return ucs_stats_serialize_binary(stream, root, sel); + } else { + return ucs_stats_serialize_text_recurs(stream, root, sel, 0); + } +} + +static ucs_status_t +ucs_stats_deserialize_recurs(FILE *stream, ucs_stats_class_t **classes, + size_t headroom, ucs_stats_node_t **p_root) +{ + ucs_stats_node_t *node, *child; + ucs_stats_class_t *cls; + uint8_t clsid, namelen; + ucs_status_t status; + void *ptr; + + if (feof(stream)) { + ucs_error("Error parsing statistics - premature end of stream"); + return UCS_ERR_MESSAGE_TRUNCATED; + } + + FREAD_ONE(&clsid, stream); + if (clsid == UCS_STATS_CLSID_SENTINEL) { + return UCS_ERR_NO_MESSAGE; /* Sentinel */ + } + + FREAD_ONE(&namelen, stream); + if (namelen >= UCS_STAT_NAME_MAX) { + ucs_error("Error parsing statistics - node name too long"); + return UCS_ERR_OUT_OF_RANGE; /* Name too long */ + } + + cls = classes[clsid]; + ptr = malloc(headroom + sizeof *node + sizeof(ucs_stats_counter_t) * cls->num_counters); + if (ptr == NULL) { + return UCS_ERR_NO_MEMORY; + } + + node = ptr + headroom; + + node->cls = cls; + FREAD(node->name, namelen, stream); + node->name[namelen] = '\0'; + ucs_list_head_init(&node->children[UCS_STATS_INACTIVE_CHILDREN]); + ucs_list_head_init(&node->children[UCS_STATS_ACTIVE_CHILDREN]); + + /* Read counters */ + ucs_stats_read_counters(node->counters, cls->num_counters, stream); + + /* Read children */ + do { + status = ucs_stats_deserialize_recurs(stream, classes, 0, &child); + if (status == UCS_OK) { + ucs_list_add_tail(&node->children[UCS_STATS_ACTIVE_CHILDREN], &child->list); + } else if (status == UCS_ERR_NO_MESSAGE) { + break; /* Sentinel */ + } else { + free(node); /* Error TODO free previous children */ + return status; + } + } while (1); + + *p_root = node; + return UCS_OK; +} + +static void ucs_stats_free_classes(ucs_stats_class_t **classes, unsigned num_classes) +{ + unsigned i, j; + + for (i = 0; i < num_classes; ++i) { + free((char*)classes[i]->name); + for (j = 0; j < classes[i]->num_counters; ++j) { + free((char*)classes[i]->counter_names[j]); + } + free(classes[i]); + } + free(classes); +} + +ucs_status_t ucs_stats_deserialize(FILE *stream, ucs_stats_node_t **p_root) +{ + ucs_stats_data_header_t hdr; + ucs_stats_root_storage_t *s; + ucs_stats_class_t **classes, *cls; + unsigned i, j, num_counters; + ucs_status_t status; + size_t nread; + char *name; + + nread = fread(&hdr, 1, sizeof(hdr), stream); + if (nread == 0) { + status = UCS_ERR_NO_ELEM; + goto err; + } + + if (hdr.version != 1) { + ucs_error("invalid file version"); + status = UCS_ERR_UNSUPPORTED; + goto err; + } + + if (!(hdr.num_classes < UINT8_MAX)) { + ucs_error("invalid num classes"); + status = UCS_ERR_OUT_OF_RANGE; + goto err; + } + + /* Read classes */ + classes = malloc(hdr.num_classes * sizeof(*classes)); + for (i = 0; i < hdr.num_classes; ++i) { + name = ucs_stats_read_str(stream); + FREAD_ONE(&num_counters, stream); + + cls = malloc(sizeof *cls + num_counters * sizeof(cls->counter_names[0])); + cls->name = name; + cls->num_counters = num_counters; + + for (j = 0; j < cls->num_counters; ++j) { + cls->counter_names[j] = ucs_stats_read_str(stream); + } + classes[i] = cls; + + } + + /* Read nodes */ + status = ucs_stats_deserialize_recurs(stream, classes, + sizeof(ucs_stats_root_storage_t) - sizeof(ucs_stats_node_t), + p_root); + if (status != UCS_OK) { + if (status == UCS_ERR_NO_MESSAGE) { + ucs_error("Error parsing statistics - misplaced sentinel"); + } + goto err_free; + } + + s = ucs_container_of(*p_root, ucs_stats_root_storage_t, node); + s->num_classes = hdr.num_classes; + s->classes = classes; + return UCS_OK; + +err_free: + ucs_stats_free_classes(classes, hdr.num_classes); +err: + return status; +} + +static void ucs_stats_free_recurs(ucs_stats_node_t *node) +{ + ucs_stats_node_t *child, *tmp; + + ucs_list_for_each_safe(child, tmp, &node->children[UCS_STATS_ACTIVE_CHILDREN], list) { + ucs_stats_free_recurs(child); + free(child); + } + ucs_list_for_each_safe(child, tmp, &node->children[UCS_STATS_INACTIVE_CHILDREN], list) { + ucs_stats_free_recurs(child); + free(child); + } +} + +void ucs_stats_free(ucs_stats_node_t *root) +{ + ucs_stats_root_storage_t *s; + + s = ucs_container_of(root, ucs_stats_root_storage_t, node); + ucs_stats_free_recurs(&s->node); + ucs_stats_free_classes(s->classes, s->num_classes); + free(s); +} + diff --git a/src/ucs/stats/stats.c b/src/ucs/stats/stats.c new file mode 100644 index 00000000000..e127e2fad3f --- /dev/null +++ b/src/ucs/stats/stats.c @@ -0,0 +1,444 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#define _GNU_SOURCE +#include "stats.h" + +#include +#include +#include +#include + +#include +#include + + +enum { + UCS_STATS_FLAG_ON_EXIT = UCS_BIT(0), + UCS_STATS_FLAG_ON_TIMER = UCS_BIT(1), + UCS_STATS_FLAG_ON_SIGNAL = UCS_BIT(2), + + UCS_STATS_FLAG_SOCKET = UCS_BIT(8), + UCS_STATS_FLAG_STREAM = UCS_BIT(9), + UCS_STATS_FLAG_STREAM_CLOSE = UCS_BIT(10), + UCS_STATS_FLAG_STREAM_BINARY = UCS_BIT(11), +}; + +enum { + UCS_ROOT_STATS_RUNTIME, + UCS_ROOT_STATS_LAST +}; + +typedef struct { + volatile unsigned flags; + + ucs_time_t start_time; + ucs_stats_node_t root_node; + ucs_stats_counter_t root_counters[UCS_ROOT_STATS_LAST]; + + union { + FILE *stream; /* Output stream */ + ucs_stats_client_h client; /* UDP client */ + }; + + union { + int signo; + double interval; + }; + + pthread_mutex_t lock; + pthread_t thread; +} ucs_stats_context_t; + +static ucs_stats_context_t ucs_stats_context = { + .flags = 0, + .root_node = {}, + .lock = PTHREAD_MUTEX_INITIALIZER, + .thread = 0xfffffffful +}; + +static ucs_stats_class_t ucs_stats_root_node_class = { + .name = "", + .num_counters = UCS_ROOT_STATS_LAST, + .counter_names = { + [UCS_ROOT_STATS_RUNTIME] = "runtime" + } +}; + + +static inline int +ucs_sys_futex(volatile void *addr1, int op, int val1, struct timespec *timeout, + void *uaddr2, int val3) +{ + return syscall(SYS_futex, addr1, op, val1, timeout, uaddr2, val3); +} + +static void ucs_stats_node_add(ucs_stats_node_t *node, ucs_stats_node_t *parent) +{ + ucs_assert(node != &ucs_stats_context.root_node); + + /* Append node to existing tree */ + pthread_mutex_lock(&ucs_stats_context.lock); + if (parent == NULL) { + parent = &ucs_stats_context.root_node; + } + ucs_list_add_tail(&parent->children[UCS_STATS_ACTIVE_CHILDREN], &node->list); + node->parent = parent; + + pthread_mutex_unlock(&ucs_stats_context.lock); +} + +static void ucs_stats_node_remove(ucs_stats_node_t *node, int make_inactive) +{ + ucs_assert(node != &ucs_stats_context.root_node); + + if (!ucs_list_is_empty(&node->children[UCS_STATS_ACTIVE_CHILDREN])) { + ucs_warn("stats node "UCS_STATS_NODE_FMT" still has active children", + UCS_STATS_NODE_ARG(node)); + } + + pthread_mutex_lock(&ucs_stats_context.lock); + ucs_list_del(&node->list); + if (make_inactive) { + ucs_list_add_tail(&node->parent->children[UCS_STATS_INACTIVE_CHILDREN], &node->list); + } + pthread_mutex_unlock(&ucs_stats_context.lock); +} + +static void ucs_stats_node_init_root(const char *name, ...) +{ + ucs_status_t status; + va_list ap; + + if (!ucs_stats_is_active()) { + return; + } + + va_start(ap, name); + status = ucs_stats_node_initv(&ucs_stats_context.root_node, + &ucs_stats_root_node_class, name, ap); + ucs_assert_always(status == UCS_OK); + va_end(ap); + + ucs_stats_context.root_node.parent = NULL; +} + +static ucs_status_t ucs_stats_node_new(ucs_stats_class_t *cls, ucs_stats_node_t **p_node) +{ + ucs_stats_node_t *node; + + node = ucs_malloc(sizeof(ucs_stats_node_t) + + sizeof(ucs_stats_counter_t) * cls->num_counters, + "stats node"); + if (node == NULL) { + ucs_error("Failed to allocate stats node for %s", cls->name); + return UCS_ERR_NO_MEMORY; + } + + *p_node = node; + return UCS_OK; +} + +ucs_status_t ucs_stats_node_alloc(ucs_stats_node_t** p_node, ucs_stats_class_t *cls, + ucs_stats_node_t *parent, const char *name, ...) +{ + ucs_stats_node_t *node; + ucs_status_t status; + va_list ap; + + if (!ucs_stats_is_active()) { + *p_node = NULL; + return UCS_OK; + } + + status = ucs_stats_node_new(cls, &node); + if (status != UCS_OK) { + return status; + } + + va_start(ap, name); + status = ucs_stats_node_initv(node, cls, name, ap); + va_end(ap); + + if (status != UCS_OK) { + ucs_free(node); + return status; + } + + ucs_trace("allocated stats node '"UCS_STATS_NODE_FMT"'", UCS_STATS_NODE_ARG(node)); + + ucs_stats_node_add(node, parent); + *p_node = node; + return UCS_OK; +} + +void ucs_stats_node_free(ucs_stats_node_t *node) +{ + if (node == NULL) { + return; + } + + ucs_trace("releasing stats node '"UCS_STATS_NODE_FMT"'", UCS_STATS_NODE_ARG(node)); + + /* If we would dump stats in exit, keep this data instead of releasing it */ + if (ucs_stats_context.flags & UCS_STATS_FLAG_ON_EXIT) { + ucs_stats_node_remove(node, 1); + } else { + ucs_stats_node_remove(node, 0); + ucs_free(node); + } +} + +static void __ucs_stats_dump(int inactive) +{ + ucs_status_t status = UCS_OK; + int options; + + /* Assume locked */ + + UCS_STATS_SET_TIME(&ucs_stats_context.root_node, UCS_ROOT_STATS_RUNTIME, + ucs_stats_context.start_time); + + if (ucs_stats_context.flags & UCS_STATS_FLAG_SOCKET) { + status = ucs_stats_client_send(ucs_stats_context.client, + &ucs_stats_context.root_node, + ucs_get_time()); + } + + if (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM) { + options = 0; + if (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM_BINARY) { + options |= UCS_STATS_SERIALIZE_BINARY; + } + if (inactive) { + options |= UCS_STATS_SERIALIZE_INACTVIVE; + } + + status = ucs_stats_serialize(ucs_stats_context.stream, + &ucs_stats_context.root_node, options); + fflush(ucs_stats_context.stream); + } + + if (status != UCS_OK) { + ucs_warn("Failed to dump statistics: %s", ucs_status_string(status)); + } +} + +static void* ucs_stats_thread_func(void *arg) +{ + struct timespec timeout, *ptime; + unsigned flags; + long nsec; + + if (ucs_stats_context.interval > 0) { + nsec = (long)(ucs_stats_context.interval * UCS_NSEC_PER_SEC + 0.5); + timeout.tv_sec = nsec / UCS_NSEC_PER_SEC; + timeout.tv_nsec = nsec % UCS_NSEC_PER_SEC; + ptime = &timeout; + } + else { + ptime = NULL; + } + + flags = ucs_stats_context.flags; + while (flags & UCS_STATS_FLAG_ON_TIMER) { + /* Wait for timeout/wakeup */ + ucs_sys_futex(&ucs_stats_context.flags, FUTEX_WAIT, flags, ptime, NULL, 0); + ucs_stats_dump(); + flags = ucs_stats_context.flags; + } + + return NULL; +} + +static void ucs_stats_open_dest() +{ + ucs_status_t status; + char *copy_str, *saveptr; + const char *hostname, *port_str; + const char *next_token; + int need_close; + + if (!strncmp(ucs_global_opts.stats_dest, "udp:", 4)) { + + copy_str = strdupa(&ucs_global_opts.stats_dest[4]); + saveptr = NULL; + hostname = strtok_r(copy_str, ":", &saveptr); + port_str = strtok_r(NULL, ":", &saveptr); + + if (hostname == NULL) { + ucs_error("Invalid statistics destination format (%s)", ucs_global_opts.stats_dest); + return; + } + + status = ucs_stats_client_init(hostname, + port_str ? atoi(port_str) : UCS_STATS_DEFAULT_UDP_PORT, + &ucs_stats_context.client); + if (status != UCS_OK) { + return; + } + + ucs_stats_context.flags |= UCS_STATS_FLAG_SOCKET; + } else if (strcmp(ucs_global_opts.stats_dest, "") != 0) { + status = ucs_open_output_stream(ucs_global_opts.stats_dest, + &ucs_stats_context.stream, + &need_close, &next_token); + if (status != UCS_OK) { + return; + } + + /* File flags */ + ucs_stats_context.flags |= UCS_STATS_FLAG_STREAM; + if (need_close) { + ucs_stats_context.flags |= UCS_STATS_FLAG_STREAM_CLOSE; + } + + /* Optional: Binary mode */ + if (!strcmp(next_token, ":bin")) { + ucs_stats_context.flags |= UCS_STATS_FLAG_STREAM_BINARY; + } + } +} + +static void ucs_stats_close_dest() +{ + if (ucs_stats_context.flags & UCS_STATS_FLAG_SOCKET) { + ucs_stats_context.flags &= ~UCS_STATS_FLAG_SOCKET; + ucs_stats_client_cleanup(ucs_stats_context.client); + } + if (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM) { + fflush(ucs_stats_context.stream); + if (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM_CLOSE) { + fclose(ucs_stats_context.stream); + } + ucs_stats_context.flags &= ~(UCS_STATS_FLAG_STREAM| + UCS_STATS_FLAG_STREAM_BINARY| + UCS_STATS_FLAG_STREAM_CLOSE); + } +} + +static void ucs_stats_dump_sighandler(int signo) +{ + ucs_stats_dump(); +} + +static void ucs_stats_set_trigger() +{ + char *p; + + if (!strcmp(ucs_global_opts.stats_trigger, "exit")) { + ucs_stats_context.flags |= UCS_STATS_FLAG_ON_EXIT; + } else if (!strncmp(ucs_global_opts.stats_trigger, "timer:", 6)) { + p = ucs_global_opts.stats_trigger + 6; + if (!ucs_config_sscanf_time(p, &ucs_stats_context.interval, NULL)) { + ucs_error("Invalid statistics interval time format: %s", p); + return; + } + + ucs_stats_context.flags |= UCS_STATS_FLAG_ON_TIMER; + pthread_create(&ucs_stats_context.thread, NULL, ucs_stats_thread_func, NULL); + } else if (!strncmp(ucs_global_opts.stats_trigger, "signal:", 7)) { + p = ucs_global_opts.stats_trigger + 7; + if (!ucs_config_sscanf_signo(p, &ucs_stats_context.signo, NULL)) { + ucs_error("Invalid statistics signal specification: %s", p); + return; + } + + signal(ucs_stats_context.signo, ucs_stats_dump_sighandler); + ucs_stats_context.flags |= UCS_STATS_FLAG_ON_SIGNAL; + } else if (!strcmp(ucs_global_opts.stats_trigger, "")) { + /* No external trigger */ + } else { + ucs_error("Invalid statistics trigger: %s", ucs_global_opts.stats_trigger); + } +} + +static void ucs_stats_unset_trigger() +{ + void *result; + + if (ucs_stats_context.flags & UCS_STATS_FLAG_ON_TIMER) { + ucs_stats_context.flags &= ~UCS_STATS_FLAG_ON_TIMER; + ucs_sys_futex(&ucs_stats_context.flags, FUTEX_WAKE, 1, NULL, NULL, 0); + pthread_join(ucs_stats_context.thread, &result); + } + + if (ucs_stats_context.flags & UCS_STATS_FLAG_ON_EXIT) { + ucs_debug("dumping stats"); + __ucs_stats_dump(1); + ucs_stats_context.flags &= ~UCS_STATS_FLAG_ON_EXIT; + } + + if (ucs_stats_context.flags & UCS_STATS_FLAG_ON_SIGNAL) { + ucs_stats_context.flags &= ~UCS_STATS_FLAG_ON_SIGNAL; + signal(ucs_stats_context.signo, SIG_DFL); + } +} + +static void ucs_stats_clean_node_recurs(ucs_stats_node_t *node) +{ + ucs_stats_node_t *child, *tmp; + + if (!ucs_list_is_empty(&node->children[UCS_STATS_ACTIVE_CHILDREN])) { + ucs_warn("stats node "UCS_STATS_NODE_FMT" still has active children", + UCS_STATS_NODE_ARG(node)); + } + + ucs_list_for_each_safe(child, tmp, &node->children[UCS_STATS_INACTIVE_CHILDREN], list) { + ucs_stats_clean_node_recurs(child); + ucs_stats_node_remove(child, 0); + ucs_free(child); + } +} + +void ucs_stats_init() +{ + ucs_assert(ucs_stats_context.flags == 0); + ucs_stats_open_dest(); + + if (!ucs_stats_is_active()) { + ucs_trace("statistics disabled"); + return; + } + + UCS_STATS_START_TIME(ucs_stats_context.start_time); + ucs_stats_node_init_root("%s:%d", ucs_get_host_name(), getpid()); + ucs_stats_set_trigger(); + + ucs_debug("statistics enabled, flags: %c%c%c%c%c%c%c", + (ucs_stats_context.flags & UCS_STATS_FLAG_ON_TIMER) ? 't' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_ON_EXIT) ? 'e' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_ON_SIGNAL) ? 's' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_SOCKET) ? 'u' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM) ? 'f' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM_BINARY) ? 'b' : '-', + (ucs_stats_context.flags & UCS_STATS_FLAG_STREAM_CLOSE) ? 'c' : '-'); +} + +void ucs_stats_cleanup() +{ + if (!ucs_stats_is_active()) { + return; + } + + ucs_stats_unset_trigger(); + ucs_stats_clean_node_recurs(&ucs_stats_context.root_node); + ucs_stats_close_dest(); + ucs_assert(ucs_stats_context.flags == 0); +} + +void ucs_stats_dump() +{ + pthread_mutex_lock(&ucs_stats_context.lock); + __ucs_stats_dump(0); + pthread_mutex_unlock(&ucs_stats_context.lock); +} + +int ucs_stats_is_active() +{ + return ucs_stats_context.flags & (UCS_STATS_FLAG_SOCKET|UCS_STATS_FLAG_STREAM); +} diff --git a/src/ucs/stats/stats.h b/src/ucs/stats/stats.h new file mode 100644 index 00000000000..9f293bb28d4 --- /dev/null +++ b/src/ucs/stats/stats.h @@ -0,0 +1,107 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#ifndef UCS_STATS_H_ +#define UCS_STATS_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +void ucs_stats_init(); +void ucs_stats_cleanup(); +void ucs_stats_dump(); +int ucs_stats_is_active(); + +#if ENABLE_STATS + +#include "libstats.h" + +/** + * Allocate statistics node. + * + * @param p_node Filled with a pointer to new node, or NULL if stats are off. + * @param cls Node class / type. + * @param parent Parent node. + * @param name Node name format. + */ +ucs_status_t ucs_stats_node_alloc(ucs_stats_node_t** p_node, ucs_stats_class_t *cls, + ucs_stats_node_t *parent, const char *name, ...); +void ucs_stats_node_free(ucs_stats_node_t *node); + + +#define UCS_STATS_ARG(_arg) ,_arg + +#define UCS_STATS_NODE_DECLARE(_node) \ + ucs_stats_node_t* _node + +#define UCS_STATS_NODE_ALLOC(_p_node, _class, _parent, ...) \ + ucs_stats_node_alloc(_p_node, _class, _parent, ## __VA_ARGS__ , "") + +#define UCS_STATS_NODE_FREE(_node) \ + ucs_stats_node_free(_node) + +#define UCS_STATS_UPDATE_COUNTER(_node, _index, _delta) \ + if (((_delta) != 0) && ((_node) != NULL)) { \ + (_node)->counters[(_index)] += (_delta); \ + } + +#define UCS_STATS_SET_COUNTER(_node, _index, _value) \ + if ((_node) != NULL) { \ + (_node)->counters[(_index)] = (_value); \ + } + +#define UCS_STATS_GET_COUNTER(_node, _index) \ + (((_node) != NULL) ? \ + (_node)->counters[(_index)] : 0) + +#define UCS_STATS_UPDATE_MAX(_node, _index, _value) \ + if ((_node) != NULL) { \ + if ((_node)->counters[(_index)] < (_value)) { \ + (_node)->counters[(_index)] = (_value); \ + } \ + } + +#define UCS_STATS_START_TIME(_start_time) \ + { \ + _start_time = ucs_get_time(); \ + ucs_compiler_fence(); \ + } + +#define UCS_STATS_UPDATE_TIME(_node, _index, _start_time) \ + { \ + ucs_compiler_fence(); \ + UCS_STATS_UPDATE_COUNTER(_node, _index, \ + (long)ucs_time_to_nsec(ucs_get_time() - (_start_time))); \ + } + +#define UCS_STATS_SET_TIME(_node, _index, _start_time) \ + { \ + ucs_compiler_fence(); \ + UCS_STATS_SET_COUNTER(_node, _index, \ + (long)ucs_time_to_nsec(ucs_get_time() - (_start_time))); \ + } + +#else + +#define UCS_STATS_ARG(_arg) +#define UCS_STATS_NODE_DECLARE(_node) +#define UCS_STATS_NODE_ALLOC(_p_node, _class, _parent, ...) UCS_OK +#define UCS_STATS_NODE_FREE(_node) +#define UCS_STATS_UPDATE_COUNTER(_node, _index, _delta) +#define UCS_STATS_SET_COUNTER(_node, _index, _value) +#define UCS_STATS_GET_COUNTER(_node, _index) 0 +#define UCS_STATS_UPDATE_MAX(_node, _index, _value) +#define UCS_STATS_START_TIME(_start_time) +#define UCS_STATS_UPDATE_TIME(_node, _index, _start_time) +#define UCS_STATS_SET_TIME(_node, _index, _start_time) + +#endif + +#endif diff --git a/src/ucs/stats/stats_parser.c b/src/ucs/stats/stats_parser.c new file mode 100644 index 00000000000..843a1c0f52c --- /dev/null +++ b/src/ucs/stats/stats_parser.c @@ -0,0 +1,53 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "stats.h" + +/* + * Dump binary statistics file to stdout. + * Usage: ucs_stats_parser [ file1 ] [ file2 ] ... + */ + +static ucs_status_t dump_file(const char *filename) +{ + ucs_stats_node_t *root; + ucs_status_t status; + FILE *stream; + + stream = fopen(filename, "rb"); + if (stream == NULL) { + fprintf(stderr, "Could not open %s\n", filename); + return UCS_ERR_IO_ERROR; + } + + while (!feof(stream)) { + status = ucs_stats_deserialize(stream, &root); + if (status != UCS_OK) { + goto out; + } + + ucs_stats_serialize(stdout, root, 0); + ucs_stats_free(root); + } + + status = UCS_OK; + +out: + fclose(stream); + return status; +} + +int main(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; ++i) { + dump_file(argv[i]); + } + + return 0; +} diff --git a/src/ucs/stats/stats_reader.c b/src/ucs/stats/stats_reader.c new file mode 100644 index 00000000000..9ea47abd77a --- /dev/null +++ b/src/ucs/stats/stats_reader.c @@ -0,0 +1,266 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "libstats.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + COL_NAME = 0, + COL_VALUE = 1, + COL_LOG_VALUE = 2, + NUM_COLS +}; + + +static ucs_stats_server_h g_stats_server; +static GtkTreeStore *g_treestore; +static GtkWidget *g_treeview; + + +static void fill_model(ucs_stats_node_t *node, GtkTreeIter *parent, int depth) +{ + GtkTreeIter tree_elem, counter_elem; + ucs_stats_counter_t value; + ucs_stats_node_t *child; + char buf[128]; + const char *name; + double log_value; + unsigned i; + + if (node == NULL) { + return; + } + + g_snprintf(buf, sizeof(buf), UCS_STATS_NODE_FMT, UCS_STATS_NODE_ARG(node)); + + gtk_tree_store_append(g_treestore, &tree_elem, parent); + gtk_tree_store_set(g_treestore, &tree_elem, COL_NAME, buf, -1); + + for (i = 0; i < node->cls->num_counters; ++i) { + gtk_tree_store_append(g_treestore, &counter_elem, &tree_elem); + + name = node->cls->counter_names[i]; + value = node->counters[i]; + + g_snprintf(buf, sizeof(buf), "%" PRIu64, value); + gtk_tree_store_set(g_treestore, &counter_elem, + COL_NAME, name, + COL_VALUE, buf, + -1); + + if (value > 999) { + log_value = ucs_log2(value); + g_snprintf(buf, sizeof(buf), "%.2f", log_value); + gtk_tree_store_set(g_treestore, &counter_elem, + COL_LOG_VALUE, buf, + -1); + } + } + + list_for_each(child, &node->children[UCS_STATS_ACTIVE_CHILDREN], list) { + fill_model(child, &tree_elem, depth + 1); + } +} + +static gboolean update_view(GtkCellRenderer *renderer) +{ + ucs_stats_node_t *root; + list_link_t *stats; + GtkTreeModel *model; + + /* Get stats */ + stats = ucs_stats_server_get_stats(g_stats_server); + + /* Detach model from view */ + model = gtk_tree_view_get_model(GTK_TREE_VIEW(g_treeview)); + g_object_ref(model); + gtk_tree_view_set_model(GTK_TREE_VIEW(g_treeview), NULL); + + /* Re-fill the model */ + gtk_tree_store_clear(g_treestore); + list_for_each(root, stats, list) { + fill_model(root, NULL, 0); + } + + /* Re-attach model to view */ + gtk_tree_view_set_model(GTK_TREE_VIEW(g_treeview), model); + g_object_unref(model); + gtk_tree_view_expand_all(GTK_TREE_VIEW(g_treeview)); + + /* Release stats */ + ucs_stats_server_purge_stats(g_stats_server); + return TRUE; +} + +static void name_cell_data_func (GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + if (gtk_tree_store_iter_depth(g_treestore, iter) == 0) { + g_object_set(renderer, + "weight", PANGO_WEIGHT_BOLD, + "weight-set", TRUE, + "foreground", "blue", + "foreground-set", TRUE, + NULL); + } else { + g_object_set(renderer, + "weight-set", FALSE, + "foreground-set", FALSE, + NULL); + } +} + + void value_cell_data_func (GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gchar *value; + + gtk_tree_model_get(model, iter, COL_VALUE, &value, -1); + + if ((value == NULL) || (strcmp(value, "0") == 0)) { + g_object_set(renderer, + "weight-set", FALSE, + NULL); + } else { + g_object_set(renderer, + "weight", PANGO_WEIGHT_BOLD, + "weight-set", TRUE, + NULL); + } + + g_free(value); +} + +static GtkWidget *create_tree_view(void) +{ + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + GtkWidget *view; + + view = gtk_tree_view_new(); + + /* Name column */ + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, "Counter"); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME); + + gtk_tree_view_column_set_min_width(col, 300); + gtk_tree_view_column_set_cell_data_func(col, renderer, name_cell_data_func, + NULL, NULL); + + /* Value column */ + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, "Value"); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_add_attribute(col, renderer, "text", COL_VALUE); + + gtk_tree_view_column_set_min_width(col, 200); + gtk_tree_view_column_set_resizable(col, TRUE); + gtk_tree_view_column_set_cell_data_func(col, renderer, value_cell_data_func, + NULL, NULL); + + /* Log2 value column*/ + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, "Log2"); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_add_attribute(col, renderer, "text", COL_LOG_VALUE); + + gtk_tree_view_column_set_resizable(col, TRUE); + gtk_tree_view_column_set_cell_data_func(col, renderer, value_cell_data_func, + NULL, NULL); + + /* Disable selecting tree items */ + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), + GTK_SELECTION_SINGLE); + + g_object_set(view, + "level-indentation", 20, + "show-expanders", FALSE, + NULL); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); + return view; +} + +void usage() { + printf("Usage: ucs_stats_reader [ -p ] [ -d ]\n"); +} + +int main(int argc, char **argv) +{ + GtkWidget *window; + ucs_status_t status; + int port; + char c; + + gtk_init(&argc, &argv); + + port = UCS_STATS_DEFAULT_UDP_PORT; + + while ((c = getopt(argc, argv, "p:h")) != -1) { + switch (c) { + case 'p': + port = atoi(optarg); + break; + case 'h': + default: + usage(); + return -1; + } + } + + status = ucs_stats_server_start(port, &g_stats_server); + if (status != UCS_OK) { + return -1; + } + + /* Create window */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); +// gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); +// gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */ + + g_treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + g_treeview = create_tree_view(); + gtk_tree_view_set_model(GTK_TREE_VIEW(g_treeview), GTK_TREE_MODEL(g_treestore)); + g_object_unref(g_treestore); /* destroy model automatically with view */ + + gtk_container_add(GTK_CONTAINER(window), g_treeview); + gtk_widget_show_all(window); + + g_timeout_add(500, (GSourceFunc) update_view, NULL); + gtk_main(); + + ucs_stats_server_destroy(g_stats_server); + return 0; +} diff --git a/src/ucs/sys/arch.h b/src/ucs/sys/arch.h new file mode 100644 index 00000000000..223e9918d16 --- /dev/null +++ b/src/ucs/sys/arch.h @@ -0,0 +1,66 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_ARCH_H +#define UCS_ARCH_H + + +/* CPU models */ +typedef enum ucs_cpu_model { + UCS_CPU_MODEL_UNKNOWN, + UCS_CPU_MODEL_INTEL_IVYBRIDGE, + UCS_CPU_MODEL_INTEL_SANDYBRIDGE, + UCS_CPU_MODEL_INTEL_NEHALEM, + UCS_CPU_MODEL_INTEL_WESTMERE, + UCS_CPU_MODEL_LAST +} ucs_cpu_model_t; + + +/* System constants */ +#define UCS_SYS_POINTER_SIZE (sizeof(void*)) +#define UCS_SYS_PARAGRAPH_SIZE 16 + + +/* Forward declaration */ +double ucs_get_cpuinfo_clock_freq(const char *mhz_header); + + +#if defined(__x86_64__) +# include "asm/asm-x86_64.h" +#elif defined(__powerpc64__) +# include "asm/asm-ppc64.h" +#elif defined(__aarch64__) +# include "asm/asm-aarch64.h" +#else +# error "Unsupported architecture" +#endif + +/* + * Define atomic functions + */ +UCS_DEFINE_ATOMIC_ADD(8, b); +UCS_DEFINE_ATOMIC_ADD(16, w); +UCS_DEFINE_ATOMIC_ADD(32, l); +UCS_DEFINE_ATOMIC_ADD(64, q); + +UCS_DEFINE_ATOMIC_FADD(8, b); +UCS_DEFINE_ATOMIC_FADD(16, w); +UCS_DEFINE_ATOMIC_FADD(32, l); +UCS_DEFINE_ATOMIC_FADD(64, q); + +UCS_DEFINE_ATOMIC_SWAP(8, b); +UCS_DEFINE_ATOMIC_SWAP(16, w); +UCS_DEFINE_ATOMIC_SWAP(32, l); +UCS_DEFINE_ATOMIC_SWAP(64, q); + +UCS_DEFINE_ATOMIC_CSWAP(8, b); +UCS_DEFINE_ATOMIC_CSWAP(16, w); +UCS_DEFINE_ATOMIC_CSWAP(32, l); +UCS_DEFINE_ATOMIC_CSWAP(64, q); + + +#endif diff --git a/src/ucs/sys/asm/asm-aarch64.h b/src/ucs/sys/asm/asm-aarch64.h new file mode 100644 index 00000000000..7859fc58a38 --- /dev/null +++ b/src/ucs/sys/asm/asm-aarch64.h @@ -0,0 +1,88 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#ifndef UCS_ASM_AARCH64_H_ +#define UCS_ASM_AARCH64_H_ + +#include +#include +#include + + +#define UCS_SYS_CACHE_LINE_SIZE 64 + +/** + * Assume the worst - weak memory ordering. + */ +#define ucs_memory_bus_fence() ucs_fatal("unimplemented"); +#define ucs_memory_bus_store_fence() ucs_memory_bus_fence() +#define ucs_memory_bus_load_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_store_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_load_fence() ucs_memory_bus_fence() + + +static inline clock_t ucs_arch_read_hres_clock(void) +{ + struct tms accurate_clock; + times(&accurate_clock); + return accurate_clock.tms_utime + accurate_clock.tms_stime; +} + +static inline double ucs_arch_get_clocks_per_sec() +{ + return CLOCKS_PER_SEC; +} + +#define UCS_DEFINE_ATOMIC_ADD(wordsize, suffix) \ + static inline void ucs_atomic_add##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + ucs_fatal("unimplemented"); \ + } + +#define UCS_DEFINE_ATOMIC_FADD(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_fadd##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + ucs_fatal("unimplemented"); \ + return 0; \ + } + +#define UCS_DEFINE_ATOMIC_SWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_swap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + ucs_fatal("unimplemented"); \ + return 0; \ + } + +#define UCS_DEFINE_ATOMIC_CSWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_cswap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t compare, \ + uint##wordsize##_t swap) { \ + ucs_fatal("unimplemented"); \ + return 0; \ + } + +static inline unsigned ucs_ffs64(uint64_t n) +{ + ucs_fatal("unimplemented"); + return 64; +} + +static inline unsigned __ucs_ilog2_u32(uint32_t n) +{ + ucs_fatal("unimplemented"); + return 31; +} + +static inline unsigned __ucs_ilog2_u64(uint64_t n) +{ + ucs_fatal("unimplemented"); + return 63; +} + +#endif diff --git a/src/ucs/sys/asm/asm-ppc64.h b/src/ucs/sys/asm/asm-ppc64.h new file mode 100644 index 00000000000..6bf7ca148f4 --- /dev/null +++ b/src/ucs/sys/asm/asm-ppc64.h @@ -0,0 +1,114 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#ifndef UCS_ASM_PPC64_H_ +#define UCS_ASM_PPC64_H_ + +#include +#include +#ifdef HAVE_SYS_PLATFORM_PPC_H +#include +#else +/* Read the Time Base Register. */ +static inline uint64_t __ppc_get_timebase (void) +{ +#ifdef __powerpc64__ + uint64_t __tb; + /* "volatile" is necessary here, because the user expects this assembly + * isn't moved after an optimization. */ + __asm__ volatile ("mfspr %0, 268" : "=r" (__tb)); + return __tb; +#else /* not __powerpc64__ */ + uint32_t __tbu, __tbl, __tmp; \ + __asm__ volatile ("0:\n\t" + "mftbu %0\n\t" + "mftbl %1\n\t" + "mftbu %2\n\t" + "cmpw %0, %2\n\t" + "bne- 0b" + : "=r" (__tbu), "=r" (__tbl), "=r" (__tmp)); + return (((uint64_t) __tbu << 32) | __tbl); +#endif /* not __powerpc64__ */ +} + +#endif + + +#define UCS_SYS_CACHE_LINE_SIZE 128 + +/** + * Assume the worst - weak memory ordering. + */ +#define ucs_memory_bus_fence() asm volatile ("sync"::: "memory") +#define ucs_memory_bus_store_fence() ucs_memory_bus_fence() +#define ucs_memory_bus_load_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_store_fence() ucs_memory_bus_fence() +#define ucs_memory_cpu_load_fence() ucs_memory_bus_fence() + + +static inline uint64_t ucs_arch_read_hres_clock() +{ + return __ppc_get_timebase(); +} + +static inline double ucs_arch_get_clocks_per_sec() +{ +#if HAVE_DECL___PPC_GET_TIMEBASE_FREQ + return __ppc_get_timebase_freq(); +#else + return ucs_get_cpuinfo_clock_freq("timebase"); +#endif +} + +#define UCS_DEFINE_ATOMIC_ADD(wordsize, suffix) \ + static inline void ucs_atomic_add##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + __sync_add_and_fetch(ptr, value); \ + } + +#define UCS_DEFINE_ATOMIC_FADD(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_fadd##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + return __sync_fetch_and_add(ptr, value); \ + } + +#define UCS_DEFINE_ATOMIC_SWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_swap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + ucs_fatal("unimplemented"); \ + return 0; \ + } + +#define UCS_DEFINE_ATOMIC_CSWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_cswap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t compare, \ + uint##wordsize##_t swap) { \ + return __sync_val_compare_and_swap(ptr, compare, swap); \ + } + +static inline unsigned __ucs_ilog2_u32(uint32_t n) +{ + int bit; + asm ("cntlzw %0,%1" : "=r" (bit) : "r" (n)); + return 31 - bit; +} + +static inline unsigned __ucs_ilog2_u64(uint64_t n) +{ + int bit; + asm ("cntlzd %0,%1" : "=r" (bit) : "r" (n)); + return 63 - bit; +} + +static inline unsigned ucs_ffs64(uint64_t n) +{ + return __ucs_ilog2_u64(n & -n); +} + +#endif diff --git a/src/ucs/sys/asm/asm-x86_64.h b/src/ucs/sys/asm/asm-x86_64.h new file mode 100644 index 00000000000..3ba0d6ddea0 --- /dev/null +++ b/src/ucs/sys/asm/asm-x86_64.h @@ -0,0 +1,119 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_ASM_X86_64_H_ +#define UCS_ASM_X86_64_H_ + +#include +#include + +#define UCS_SYS_CACHE_LINE_SIZE 64 + +/** + * In x86_64, there is strong ordering of each processor with respect to another + * processor, but weak ordering with respect to the bus. + */ +#define ucs_memory_bus_fence() asm volatile ("mfence"::: "memory") +#define ucs_memory_bus_store_fence() asm volatile ("sfence" ::: "memory") +#define ucs_memory_bus_load_fence() asm volatile ("lfence" ::: "memory") +#define ucs_memory_cpu_fence() ucs_compiler_fence() +#define ucs_memory_cpu_store_fence() ucs_compiler_fence() +#define ucs_memory_cpu_load_fence() ucs_compiler_fence() + + +static inline uint64_t ucs_arch_read_hres_clock() +{ + uint32_t low, high; + asm volatile ("rdtsc" : "=a" (low), "=d" (high)); + return ((uint64_t)high << 32) | (uint64_t)low; +} + +static inline double ucs_arch_get_clocks_per_sec() +{ + return ucs_get_cpuinfo_clock_freq("cpu MHz"); +} + +#define UCS_DEFINE_ATOMIC_ADD(wordsize, suffix) \ + static inline void ucs_atomic_add##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + asm volatile ( \ + "lock add" #suffix " %1, %0" \ + : "+m"(*ptr) \ + : "ir" (value)); \ + } + +#define UCS_DEFINE_ATOMIC_FADD(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_fadd##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + asm volatile ( \ + "lock xadd" #suffix " %0, %1" \ + : "+r" (value), "+m" (*ptr) \ + : : "memory"); \ + return value; \ + } + +#define UCS_DEFINE_ATOMIC_SWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_swap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t value) { \ + asm volatile ( \ + "lock xchg" #suffix " %0, %1" \ + : "+r" (value), "+m" (*ptr) \ + : : "memory", "cc"); \ + return value; \ + } + +#define UCS_DEFINE_ATOMIC_CSWAP(wordsize, suffix) \ + static inline uint##wordsize##_t ucs_atomic_cswap##wordsize(volatile uint##wordsize##_t *ptr, \ + uint##wordsize##_t compare, \ + uint##wordsize##_t swap) { \ + unsigned long prev; \ + asm volatile ( \ + "lock cmpxchg" # suffix " %1, %2" \ + : "=a" (prev) \ + : "r"(swap), "m"(*ptr), "0" (compare) \ + : "memory"); \ + return prev; \ + } + +static inline unsigned ucs_ffs64(uint64_t n) +{ + uint64_t result; + asm("bsfq %1,%0" + : "=r" (result) + : "r" (n)); + return result; +} + +static inline unsigned __ucs_ilog2_u32(uint32_t n) +{ + uint32_t result; + asm("bsrl %1,%0" + : "=r" (result) + : "r" (n)); + return result; +} + +static inline unsigned __ucs_ilog2_u64(uint64_t n) +{ + uint64_t result; + asm("bsrq %1,%0" + : "=r" (result) + : "r" (n)); + return result; +} + +static inline void ucs_cpuid(unsigned level, unsigned *a, unsigned *b, + unsigned *c, unsigned *d) +{ + asm volatile ("cpuid\n\t" + : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) + : "0" (level)); +} + +#endif + + diff --git a/src/ucs/sys/compiler.h b/src/ucs/sys/compiler.h index edb4560fdf5..809aedd495e 100644 --- a/src/ucs/sys/compiler.h +++ b/src/ucs/sys/compiler.h @@ -1,12 +1,19 @@ /** -* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. * * $COPYRIGHT$ * $HEADER$ */ -#ifndef UCS_SYS_COMPILER_H_ -#define UCS_SYS_COMPILER_H_ +#ifndef UCS_COMPILER_H_ +#define UCS_COMPILER_H_ + +#include +#include + +#ifndef ULLONG_MAX +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) +#endif #ifdef __cplusplus @@ -18,4 +25,128 @@ #endif +#ifdef __ICC +# pragma warning(disable: 268) +#endif + +/* + * Assertions which are checked in compile-time + * + * Usage: UCS_STATIC_ASSERT(condition) + */ +#define UCS_STATIC_ASSERT(_cond) \ + switch(0) {case 0:case (_cond):;} + +/* Packed structure */ +#define UCS_S_PACKED __attribute__((packed)) + +/* Aliasing structure */ +#define UCS_S_MAY_ALIAS __attribute__((may_alias)) + +/* A function without side effects */ +#define UCS_F_PURE __attribute__((pure)) + +/* A function which does not return */ +#define UCS_F_NORETURN __attribute__((noreturn)) + +/* Always inline the function */ +#ifdef __GNUC__ +#define UCS_F_ALWAYS_INLINE inline __attribute__ ((always_inline)) +#else +#define UCS_F_ALWAYS_INLINE inline +#endif + +/* Avoid inlining the function */ +#define UCS_F_NOINLINE __attribute__ ((noinline)) + +/* + * Enable compiler checks for printf-like formatting. + * + * @param fmtargN number of formatting argument + * @param vargN number of variadic argument + */ +#define UCS_F_PRINTF(fmtargN, vargN) __attribute__((format(printf, fmtargN, vargN))) + +/* Shared library constructor and destructor */ +#define UCS_F_CTOR __attribute__((constructor)) +#define UCS_F_DTOR __attribute__((destructor)) + +/* Silence "defined but not used" error for static function */ +#define UCS_F_MAYBE_UNUSED __attribute__((used)) + +/* Silence "uninitialized variable" for stupid compilers (gcc 4.1) + * which can't optimize properly. + */ +#if (((__GNUC__ == 4) && (__GNUC_MINOR__ == 1)) || !defined(__OPTIMIZE__)) +# define UCS_V_INITIALIZED(_v) (_v = (typeof(_v))0) +#else +# define UCS_V_INITIALIZED(_v) ((void)0) +#endif + +/* Unused variable */ +#define UCS_V_UNUSED __attribute__((unused)) + +/* Used for labels */ +#define UCS_EMPTY_STATEMENT {} + +/** + * @return Offset of _member in _type. _type is a structure type. + */ +#define ucs_offsetof(_type, _member) \ + ((unsigned long)&( ((_type*)0)->_member )) + +/** + * Get a pointer to a struct containing a member. + * + * @param __ptr Pointer to the member. + * @param type Container type. + * @param member Element member inside the container. + + * @return Address of the container structure. + */ +#define ucs_container_of(_ptr, _type, _member) \ + ( (_type*)( (char*)(void*)(_ptr) - ucs_offsetof(_type, _member) ) ) + + +/** + * Size of statically-declared array + */ +#define ucs_static_array_size(_array) \ + ({ \ + UCS_STATIC_ASSERT((void*)&(_array) == (void*)&((_array)[0])); \ + ( sizeof(_array) / sizeof((_array)[0]) ); \ + }) + +/** + * @return Address of a derived structure. It must have a "super" member at offset 0. + * NOTE: we use the built-in offsetof here because we can't use ucs_offsetof() in + * a constant expression. + */ +#define ucs_derived_of(_ptr, _type) \ + ({\ + UCS_STATIC_ASSERT(offsetof(_type, super) == 0) \ + ucs_container_of(_ptr, _type, super); \ + }) + +/** + * Prevent compiler from reordering instructions + */ +#define ucs_compiler_fence() asm volatile(""::: "memory") + +/** + * Prefetch cache line + */ +#define ucs_prefetch(p) __builtin_prefetch(p) + +/* Branch prediction */ +#define ucs_likely(x) __builtin_expect(x, 1) +#define ucs_unlikely(x) __builtin_expect(x, 0) + +/* Check if an expression is a compile-time constant */ +#define ucs_is_constant(expr) __builtin_constant_p(expr) + +/* Special pointer value */ +#define UCS_ERR_PTR(err) ((void*)-(err + 1)) + + #endif diff --git a/src/ucs/sys/math.c b/src/ucs/sys/math.c new file mode 100644 index 00000000000..2a720d5b695 --- /dev/null +++ b/src/ucs/sys/math.c @@ -0,0 +1,85 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "math.h" + +static uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static uint64_t ucs_large_primes[] = { + 14476643271716824181ull, 12086978239110065677ull, + 15386586898367453843ull, 17958312454893560653ull, + + 32416188191ull, 32416188793ull, + 32416189381ull, 32416190071ull, + + 9929050057ull, 9929050081ull, 9929050097ull, 9929050111ull, + 9929050121ull, 9929050133ull, 9929050139ull, 9929050163ull, + 9929050207ull, 9929050217ull, 9929050249ull, 9929050253ull +}; + +uint32_t ucs_calc_crc32(uint32_t crc, const void *buf, size_t size) +{ + const uint8_t *p = buf; + size_t i; + + crc = crc ^ ~0U; + for (i = 0; i < size; ++i) { + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + } + return crc ^ ~0U; +} + +uint64_t ucs_get_prime(unsigned index) +{ + static const unsigned num_primes = sizeof(ucs_large_primes) / sizeof(ucs_large_primes[0]); + + return ucs_large_primes[index % num_primes]; +} diff --git a/src/ucs/sys/math.h b/src/ucs/sys/math.h new file mode 100644 index 00000000000..d51fe352d36 --- /dev/null +++ b/src/ucs/sys/math.h @@ -0,0 +1,220 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + + +#ifndef UCS_MATH_H +#define UCS_MATH_H + +#include "arch.h" +#include "compiler.h" + +#include + + +#define UCS_KBYTE (1ull << 10) +#define UCS_MBYTE (1ull << 20) +#define UCS_GBYTE (1ull << 30) + +#define ucs_min(a, b) \ +({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ +}) + +#define ucs_max(a, b) \ +({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ +}) + +#define ucs_is_pow2(_n) \ + ( ((_n) > 0) && !((_n) & ((_n) - 1)) ) + +#define ucs_padding(_n, _alignment) \ + ( ((_alignment) - (_n) % (_alignment)) % (_alignment) ) + +#define ucs_align_up(_n, _alignment) \ + ( (_n) + ucs_padding(_n, _alignment) ) + +#define ucs_align_up_pow2(_n, _alignment) \ + ( ((_n) + (_alignment) - 1) & ~((_alignment) - 1) ) + +#define ucs_align_down(_n, _alignment) \ + ( (_n) - ((_n) % (_alignment)) ) + +#define ucs_roundup_pow2(n) \ + ({ \ + typeof(n) pow2; \ + ucs_assert((n) >= 1); \ + for (pow2 = 1; pow2 < (n); pow2 <<= 1); \ + pow2; \ + }) + +/* The i-th bit */ +#define UCS_BIT(i) (1ull << (i)) + +/* Mask of bits 0..i-1 */ +#define UCS_MASK(i) (UCS_BIT(i) - 1) + +#define UCS_MASK_SAFE(i) \ + (((i) >= 64) ? ((uint64_t)(-1)) : UCS_MASK(i)) + +#define ucs_div_round_up(_n, _d) \ + (((_n) + (_d) - 1) / (_d)) + +static inline double ucs_log2(double x) +{ + return log(x) / log(2.0); +} + +#define ucs_ilog2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n) < 1 ? 0 : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ + (n) & (1ULL << 60) ? 60 : \ + (n) & (1ULL << 59) ? 59 : \ + (n) & (1ULL << 58) ? 58 : \ + (n) & (1ULL << 57) ? 57 : \ + (n) & (1ULL << 56) ? 56 : \ + (n) & (1ULL << 55) ? 55 : \ + (n) & (1ULL << 54) ? 54 : \ + (n) & (1ULL << 53) ? 53 : \ + (n) & (1ULL << 52) ? 52 : \ + (n) & (1ULL << 51) ? 51 : \ + (n) & (1ULL << 50) ? 50 : \ + (n) & (1ULL << 49) ? 49 : \ + (n) & (1ULL << 48) ? 48 : \ + (n) & (1ULL << 47) ? 47 : \ + (n) & (1ULL << 46) ? 46 : \ + (n) & (1ULL << 45) ? 45 : \ + (n) & (1ULL << 44) ? 44 : \ + (n) & (1ULL << 43) ? 43 : \ + (n) & (1ULL << 42) ? 42 : \ + (n) & (1ULL << 41) ? 41 : \ + (n) & (1ULL << 40) ? 40 : \ + (n) & (1ULL << 39) ? 39 : \ + (n) & (1ULL << 38) ? 38 : \ + (n) & (1ULL << 37) ? 37 : \ + (n) & (1ULL << 36) ? 36 : \ + (n) & (1ULL << 35) ? 35 : \ + (n) & (1ULL << 34) ? 34 : \ + (n) & (1ULL << 33) ? 33 : \ + (n) & (1ULL << 32) ? 32 : \ + (n) & (1ULL << 31) ? 31 : \ + (n) & (1ULL << 30) ? 30 : \ + (n) & (1ULL << 29) ? 29 : \ + (n) & (1ULL << 28) ? 28 : \ + (n) & (1ULL << 27) ? 27 : \ + (n) & (1ULL << 26) ? 26 : \ + (n) & (1ULL << 25) ? 25 : \ + (n) & (1ULL << 24) ? 24 : \ + (n) & (1ULL << 23) ? 23 : \ + (n) & (1ULL << 22) ? 22 : \ + (n) & (1ULL << 21) ? 21 : \ + (n) & (1ULL << 20) ? 20 : \ + (n) & (1ULL << 19) ? 19 : \ + (n) & (1ULL << 18) ? 18 : \ + (n) & (1ULL << 17) ? 17 : \ + (n) & (1ULL << 16) ? 16 : \ + (n) & (1ULL << 15) ? 15 : \ + (n) & (1ULL << 14) ? 14 : \ + (n) & (1ULL << 13) ? 13 : \ + (n) & (1ULL << 12) ? 12 : \ + (n) & (1ULL << 11) ? 11 : \ + (n) & (1ULL << 10) ? 10 : \ + (n) & (1ULL << 9) ? 9 : \ + (n) & (1ULL << 8) ? 8 : \ + (n) & (1ULL << 7) ? 7 : \ + (n) & (1ULL << 6) ? 6 : \ + (n) & (1ULL << 5) ? 5 : \ + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ + (n) & (1ULL << 1) ? 1 : \ + (n) & (1ULL << 0) ? 0 : \ + 0 \ + ) : \ + (sizeof(n) <= 4) ? \ + __ucs_ilog2_u32((uint32_t)(n)) : \ + __ucs_ilog2_u64((uint64_t)(n)) \ +) + + +/** + * Convert flags without a branch + * @return 'newflag' oldflag is set in 'value', otherwise - 0 + */ +#define ucs_convert_flag(value, oldflag, newflag) \ + ({ \ + UCS_STATIC_ASSERT(ucs_is_constant(oldflag)); \ + UCS_STATIC_ASSERT(ucs_is_constant(newflag)); \ + UCS_STATIC_ASSERT(ucs_is_pow2(oldflag)); \ + UCS_STATIC_ASSERT(ucs_is_pow2(newflag)); \ + (((value) & (oldflag)) ? (newflag) : 0); \ + }) + + +/** + * Test if a value is one of a specified list of values, assuming all possible + * values are powers of 2. + */ +#define __ucs_test_flags(__value, __f1, __f2, __f3, __f4, __f5, __f6, __f7, __f8, __f9, ...) \ + (__value & ((__f1) | (__f2) | (__f3) | (__f4) | (__f5) | (__f6) | (__f7) | (__f8) | (__f9))) +#define ucs_test_flags(__value, ...) \ + __ucs_test_flags((__value), __VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +/* + * Check if all given flags are on + */ +#define ucs_test_all_flags(__value, __mask) \ + ( ((__value) & (__mask)) == (__mask) ) + +/* Returns the number of 1-bits in x */ +#define ucs_count_one_bits(x) __builtin_popcount(x) + +/* Returns the number of trailing 0-bits in x, starting at the least + * significant bit position. If x is 0, the result is undefined. + */ +#define ucs_count_zero_bits(x) __builtin_ctz(x) + +/** + * Compare unsigned numbers which can wrap-around, assuming the wrap-around + * distance can be at most the maximal value of the signed type. + * + * @param __a First number + * @param __op Operator (e.g >=) + * @param __b Second number + * @param _signed_type Signed type of __a/__b (e.g int_32_t) + * + * @return value of the expression "__a __op __b". + */ +#define UCS_CIRCULAR_COMPARE(__a, __op, __b, __signed_type) \ + ((__signed_type)((__a) - (__b)) __op 0) + +#define UCS_CIRCULAR_COMPARE16(__a, __op, __b) UCS_CIRCULAR_COMPARE(__a, __op, __b, int16_t) +#define UCS_CIRCULAR_COMPARE32(__a, __op, __b) UCS_CIRCULAR_COMPARE(__a, __op, __b, int32_t) +#define UCS_CIRCULAR_COMPARE64(__a, __op, __b) UCS_CIRCULAR_COMPARE(__a, __op, __b, int64_t) + + +/** + * Calculate CRC32 of a buffer. + */ +uint32_t ucs_calc_crc32(uint32_t crc, const void *buf, size_t size); + + +/* + * Generate a large prime number + */ +uint64_t ucs_get_prime(unsigned index); + + +#endif /* MACROS_H_ */ diff --git a/src/ucs/sys/preprocessor.h b/src/ucs/sys/preprocessor.h new file mode 100644 index 00000000000..e2adca8540a --- /dev/null +++ b/src/ucs/sys/preprocessor.h @@ -0,0 +1,132 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_PREPROCESSOR_H +#define UCS_PREPROCESSOR_H + +#include + +/* Paste two expanded tokens */ +#define __UCS_TOKENPASTE_HELPER(x, y) x ## y +#define UCS_PP_TOKENPASTE(x, y) __UCS_TOKENPASTE_HELPER(x, y) + +/* Unique value generator */ +#ifdef __COUNTER__ +# define UCS_PP_UNIQUE_ID __COUNTER__ +#else +# define UCS_PP_UNIQUE_ID __LINE__ +#endif + +/* Creating unique identifiers, used for macros */ +#define UCS_PP_APPEND_UNIQUE_ID(x) UCS_PP_TOKENPASTE(x, UCS_PP_UNIQUE_ID) + +/* Convert to string */ +#define _UCS_PP_MAKE_STRING(x) #x +#define UCS_PP_MAKE_STRING(x) _UCS_PP_MAKE_STRING(x) + +/* + * Count number of macro arguments + * e.g UCS_PP_NUM_ARGS(a,b) will expand to: 2 + */ +#define UCS_PP_MAX_ARGS 15 +#define _UCS_PP_NUM_ARGS(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,N,...) \ + N +#define UCS_PP_NUM_ARGS(...) \ + _UCS_PP_NUM_ARGS(, ## __VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) + + +/* Expand macro for each argument in the list + * e.g + * UCS_PP_FOREACH(macro, arg, a, b, c) will expand to: macro(arg, a) macro(arg, b) macro(arg, c) + * UCS_PP_FOREACH_SEP(macro, arg, a, b, c) will expand to: macro(arg, a), macro(arg, b), macro(arg, c) + */ +#define UCS_PP_FOREACH(_macro, _arg, ...) \ + UCS_PP_TOKENPASTE(_UCS_PP_FOREACH_, UCS_PP_NUM_ARGS(__VA_ARGS__))(_macro, _arg, __VA_ARGS__) +#define UCS_PP_FOREACH_SEP(_macro, _arg, ...) \ + UCS_PP_TOKENPASTE(_UCS_PP_FOREACH_SEP_, UCS_PP_NUM_ARGS(__VA_ARGS__))(_macro, _arg, __VA_ARGS__) + +#define _UCS_PP_FOREACH_0(_macro , _arg, ...) +#define _UCS_PP_FOREACH_1(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_0 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_2(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_1 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_3(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_2 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_4(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_3 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_5(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_4 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_6(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_5 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_7(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_6 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_8(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_7 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_9(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_8 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_10(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_9 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_11(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_10(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_12(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_11(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_13(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_12(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_14(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_13(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_15(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_14(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_16(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_15(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_17(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_16(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_18(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_17(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_19(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_18(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_20(_macro, _arg, _arg1, ...) _macro(_arg, _arg1) _UCS_PP_FOREACH_19(_macro, _arg, __VA_ARGS__) + +#define _UCS_PP_FOREACH_SEP_1(_macro , _arg, _arg1, ...) _macro(_arg, _arg1) +#define _UCS_PP_FOREACH_SEP_2(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_1 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_3(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_2 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_4(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_3 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_5(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_4 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_6(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_5 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_7(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_6 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_8(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_7 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_9(_macro , _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_8 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_10(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_9 (_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_11(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_10(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_12(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_11(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_13(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_12(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_14(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_13(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_15(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_14(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_16(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_15(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_17(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_16(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_18(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_17(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_19(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_18(_macro, _arg, __VA_ARGS__) +#define _UCS_PP_FOREACH_SEP_20(_macro, _arg, _arg1, ...) _macro(_arg, _arg1), _UCS_PP_FOREACH_SEP_19(_macro, _arg, __VA_ARGS__) + + +/* Extract elements from tuples + */ +#define UCS_PP_TUPLE_0(_0, ...) _0 +#define UCS_PP_TUPLE_1(_0, _1, ...) _1 +#define UCS_PP_TUPLE_2(_0, _1, _2, ...) _2 +#define UCS_PP_TUPLE_3(_0, _1, _2, _3, ...) _3 +#define UCS_PP_TUPLE_4(_0, _1, _2, _3, _4, ...) _4 +#define UCS_PP_TUPLE_5(_0, _1, _2, _3, _4, _5, ...) _5 + + +/* Sequence of numbers + */ +#define _UCS_PP_SEQ_0 +#define _UCS_PP_SEQ_1 _UCS_PP_SEQ_0 0 +#define _UCS_PP_SEQ_2 _UCS_PP_SEQ_1 , 1 +#define _UCS_PP_SEQ_3 _UCS_PP_SEQ_2 , 2 +#define _UCS_PP_SEQ_4 _UCS_PP_SEQ_3 , 3 +#define _UCS_PP_SEQ_5 _UCS_PP_SEQ_4 , 4 +#define _UCS_PP_SEQ_6 _UCS_PP_SEQ_5 , 5 +#define _UCS_PP_SEQ_7 _UCS_PP_SEQ_6 , 6 +#define _UCS_PP_SEQ_8 _UCS_PP_SEQ_7 , 7 +#define _UCS_PP_SEQ_9 _UCS_PP_SEQ_8 , 8 +#define _UCS_PP_SEQ_10 _UCS_PP_SEQ_9 , 9 +#define _UCS_PP_SEQ_11 _UCS_PP_SEQ_10, 10 +#define _UCS_PP_SEQ_12 _UCS_PP_SEQ_11, 11 +#define _UCS_PP_SEQ_13 _UCS_PP_SEQ_12, 12 +#define _UCS_PP_SEQ_14 _UCS_PP_SEQ_13, 13 +#define _UCS_PP_SEQ_15 _UCS_PP_SEQ_14, 14 +#define _UCS_PP_SEQ_16 _UCS_PP_SEQ_15, 15 +#define _UCS_PP_SEQ_17 _UCS_PP_SEQ_16, 16 +#define _UCS_PP_SEQ_18 _UCS_PP_SEQ_17, 17 +#define _UCS_PP_SEQ_19 _UCS_PP_SEQ_18, 18 +#define _UCS_PP_SEQ_20 _UCS_PP_SEQ_19, 19 +#define _UCS_PP_SEQ(_n) _UCS_PP_SEQ_##_n +#define UCS_PP_SEQ(_n) _UCS_PP_SEQ(_n) + +#endif diff --git a/src/ucs/sys/sys.c b/src/ucs/sys/sys.c new file mode 100644 index 00000000000..3c9b046e541 --- /dev/null +++ b/src/ucs/sys/sys.c @@ -0,0 +1,737 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Default huge page size is 2 MBytes */ +#define UCS_DEFAULT_HUGEPAGE_SIZE (2 * 1024 * 1024) +#define UCS_PROCESS_MAPS_FILE "/proc/self/maps" + + +const char *ucs_get_host_name() +{ + static char hostname[256] = {0}; + + if (*hostname == 0) { + gethostname(hostname, sizeof(hostname)); + strtok(hostname, "."); + } + return hostname; +} + +const char *ucs_get_user_name() +{ + static char username[256] = {0}; + + if (*username == 0) { + getlogin_r(username, sizeof(username)); + } + return username; +} + +void ucs_expand_path(const char *path, char *fullpath, size_t max) +{ + char cwd[1024] = {0}; + + if (path[0] == '/') { + strncpy(fullpath, path, max); + } else if (getcwd(cwd, sizeof(cwd) - 1) != NULL) { + snprintf(fullpath, max, "%s/%s", cwd, path); + } else { + ucs_warn("failed to expand path '%s' (%s), using original path", path, + strerror(errno)); + strncpy(fullpath, path, max); + } +} + +const char *ucs_get_exe() +{ + static char exe[1024]; + int ret; + + ret = readlink("/proc/self/exe", exe, sizeof(exe) - 1); + if (ret < 0) { + exe[0] = '\0'; + } else { + exe[ret] = '\0'; + } + + return exe; +} + +uint32_t ucs_file_checksum(const char *filename) +{ + char buffer[1024]; + ssize_t nread; + int fd; + uint32_t crc; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + return 0; + } + + crc = 0; + do { + nread = read(fd, buffer, sizeof(buffer)); + if (nread > 0) { + crc = ucs_calc_crc32(crc, buffer, nread); + } + } while (nread == sizeof(buffer)); + close(fd); + + return crc; +} + +static uint64_t ucs_get_mac_address() +{ + static uint64_t mac_address = 0; + struct ifreq ifr, *it, *end; + struct ifconf ifc; + char buf[1024]; + int sock; + + if (mac_address == 0) { + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) { + ucs_error("failed to create socket: %m"); + return 0; + } + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { + ucs_error("ioctl(SIOCGIFCONF) failed: %m"); + close(sock); + return 0; + } + + it = ifc.ifc_req; + end = it + (ifc.ifc_len / sizeof *it); + for (it = ifc.ifc_req; it != end; ++it) { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ucs_error("ioctl(SIOCGIFFLAGS) failed: %m"); + close(sock); + return 0; + } + + if (!(ifr.ifr_flags & IFF_LOOPBACK)) { + if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0) { + ucs_error("ioctl(SIOCGIFHWADDR) failed: %m"); + close(sock); + return 0; + } + + memcpy(&mac_address, ifr.ifr_hwaddr.sa_data, 6); + break; + } + } + + close(sock); + ucs_trace("MAC address is 0x%012"PRIX64, mac_address); + } + + return mac_address; +} + +static uint64_t __sumup_host_name(unsigned prime_index) +{ + uint64_t sum, n; + const char *p; + unsigned i; + + sum = 0; + i = prime_index; + p = ucs_get_host_name(); + while (*p != '\0') { + n = 0; + strncpy((char*)&n, p, sizeof(n)); + sum += ucs_get_prime(i) * n; + ++i; + p += ucs_min(sizeof(n), strlen(p)); + } + return sum; +} + +uint64_t ucs_machine_guid() +{ + return ucs_get_prime(0) * ucs_get_mac_address() + + __sumup_host_name(1); +} + +int ucs_get_first_cpu() +{ + int first_cpu, total_cpus, ret; + cpu_set_t mask; + + ret = sysconf(_SC_NPROCESSORS_CONF); + if (ret < 0) { + ucs_error("failed to get local cpu count: %m"); + return ret; + } + total_cpus = ret; + + CPU_ZERO(&mask); + ret = sched_getaffinity(0, sizeof(mask), &mask); + if (ret < 0) { + ucs_error("failed to get process affinity: %m"); + return ret; + } + + for (first_cpu = 0; first_cpu < total_cpus; ++first_cpu) { + if (CPU_ISSET(first_cpu, &mask)) { + return first_cpu; + } + } + + return total_cpus; +} + +uint64_t ucs_generate_uuid(uint64_t seed) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return seed + + ucs_get_prime(0) * getpid() + + ucs_get_prime(1) * ucs_get_time() + + ucs_get_prime(2) * ucs_get_mac_address() + + ucs_get_prime(3) * tv.tv_sec + + ucs_get_prime(4) * tv.tv_usec + + __sumup_host_name(5); +} + +void ucs_fill_filename_template(const char *tmpl, char *buf, size_t max) +{ + char *p, *end; + const char *pf, *pp; + size_t length; + time_t t; + + p = buf; + end = buf + max - 1; + *end = 0; + pf = tmpl; + while (*pf != 0 && p < end) { + pp = strchr(pf, '%'); + if (pp == NULL) { + strncpy(p, pf, end - p); + p = end; + break; + } + + length = ucs_min(pp - pf, end - p); + strncpy(p, pf, length); + p += length; + + switch (*(pp + 1)) { + case 'p': + snprintf(p, end - p, "%d", getpid()); + pf = pp + 2; + p += strlen(p); + break; + case 'h': + snprintf(p, end - p, "%s", ucs_get_host_name()); + pf = pp + 2; + p += strlen(p); + break; + case 'c': + snprintf(p, end - p, "%02d", ucs_get_first_cpu()); + pf = pp + 2; + p += strlen(p); + break; + case 't': + t = time(NULL); + strftime(p, end - p, "%Y-%m-%d-%H:%M:%S", localtime(&t)); + pf = pp + 2; + p += strlen(p); + break; + case 'u': + snprintf(p, end - p, "%s", basename(ucs_get_user_name())); + pf = pp + 2; + p += strlen(p); + break; + case 'e': + snprintf(p, end - p, "%s", basename(ucs_get_exe())); + pf = pp + 2; + p += strlen(p); + break; + default: + *(p++) = *pp; + pf = pp + 1; + break; + } + + p += strlen(p); + } + *p = 0; +} + +ucs_status_t +ucs_open_output_stream(const char *config_str, FILE **p_fstream, int *p_need_close, + const char **p_next_token) +{ + FILE *output_stream; + char filename[256]; + char *template; + const char *p; + size_t len; + + *p_need_close = 0; + *p_fstream = NULL; + *p_next_token = config_str; + + len = strcspn(config_str, ":"); + if (!strncmp(config_str, "stdout", len)) { + *p_fstream = stdout; + *p_next_token = config_str + len; + } else if (!strncmp(config_str, "stderr", len)) { + *p_fstream = stderr; + *p_next_token = config_str + len; + } else { + if (!strncmp(config_str, "file:", 5)) { + p = config_str + 5; + } else { + p = config_str; + } + + len = strcspn(p, ":"); + template = strndup(p, len); + ucs_fill_filename_template(template, filename, sizeof(filename)); + free(template); + + output_stream = fopen(filename, "w"); + if (output_stream == NULL) { + ucs_error("failed to open '%s' for writing: %m", filename); + return UCS_ERR_IO_ERROR; + } + + *p_fstream = output_stream; + *p_need_close = 1; + *p_next_token = p + len; + } + + return UCS_OK; +} + +uint64_t ucs_string_to_id(const char* str) +{ + uint64_t id = 0; + strncpy((char*)&id, str, sizeof(id) - 1); /* Last character will be \0 */ + return id; +} + + +ssize_t ucs_read_file(char *buffer, size_t max, int silent, + const char *filename_fmt, ...) +{ + char filename[MAXPATHLEN]; + ssize_t read_bytes; + va_list ap; + int fd; + + va_start(ap, filename_fmt); + vsnprintf(filename, MAXPATHLEN, filename_fmt, ap); + va_end(ap); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + if (!silent) { + ucs_error("failed to open %s: %m", filename); + } + read_bytes = -1; + goto out; + } + + read_bytes = read(fd, buffer, max - 1); + if (read_bytes < 0) { + if (!silent) { + ucs_error("failed to read from %s: %m", filename); + } + goto out_close; + } + + if (read_bytes < max) { + buffer[read_bytes] = '\0'; + } + +out_close: + close(fd); +out: + return read_bytes; +} + +size_t ucs_get_page_size() +{ + static size_t page_size = 0; + + if (page_size == 0) { + page_size = sysconf(_SC_PAGESIZE); + } + return page_size; +} + +size_t ucs_get_huge_page_size() +{ + static size_t huge_page_size = 0; + char buf[256]; + int size_kb; + FILE *f; + + /* Cache the huge page size value */ + if (huge_page_size == 0) { + f = fopen("/proc/meminfo", "r"); + if (f != NULL) { + while (fgets(buf, sizeof(buf), f)) { + if (sscanf(buf, "Hugepagesize: %d kB", &size_kb) == 1) { + huge_page_size = size_kb * 1024; + break; + } + } + fclose(f); + } + + if (huge_page_size == 0) { + huge_page_size = UCS_DEFAULT_HUGEPAGE_SIZE; + ucs_warn("cannot determine huge page size, using default: %Zu", + huge_page_size); + } else { + ucs_trace("detected huge page size: %Zu", huge_page_size); + } + } + + return huge_page_size; +} + +ucs_status_t ucs_sysv_alloc(size_t *size, void **address_p, int flags, int *shmid) +{ + void *ptr; + int ret; + struct shminfo shminfo, *shminfo_ptr; + + if (RUNNING_ON_VALGRIND) { + flags &= ~SHM_HUGETLB; + } + + if (flags & SHM_HUGETLB){ + *size = ucs_align_up(*size, ucs_get_huge_page_size()); + } else { + *size = ucs_align_up(*size, ucs_get_page_size()); + } + + flags |= IPC_CREAT | SHM_R | SHM_W; + *shmid = shmget(IPC_PRIVATE, *size, flags); + if (*shmid < 0) { + switch (errno) { + case ENFILE: + case ENOMEM: + case ENOSPC: + case EPERM: + if (!(flags & SHM_HUGETLB)) { + shminfo_ptr = &shminfo; + if ((shmctl(0, IPC_INFO, (struct shmid_ds *) shminfo_ptr)) > -1) { + ucs_error("shmget failed (size=%Zu): The max number of shared memory segments in the system is = %ld. " + "Please try to increase this value through /proc/sys/kernel/shmmni", + *size, shminfo.shmmni); + } + } + + return UCS_ERR_NO_MEMORY; + break; + case EINVAL: + ucs_error("A new segment was to be created and size < SHMMIN or size > SHMMAX, " + "or no new segment was to be created. A segment with given key existed, " + "but size is greater than the size of that segment. " + "Please check shared memory limits by 'ipcs -l'."); + return UCS_ERR_NO_MEMORY; + break; + default: + ucs_error("shmget(size=%Zu, flags=0x%x) returned unexpected error: %m. " + "Please check shared memory limits by 'ipcs -l'.", + *size, flags); + return UCS_ERR_SHMEM_SEGMENT; + } + } + + /* Attach segment */ + ptr = shmat(*shmid, NULL, 0); + + /* Remove segment, the attachment keeps a reference to the mapping */ + ret = shmctl(*shmid, IPC_RMID, NULL); + if (ret != 0) { + ucs_warn("shmctl(IPC_RMID, shmid=%d) returned %d: %m", *shmid, ret); + } + + /* Check if attachment was successful */ + if (ptr == (void*)-1) { + if (errno == ENOMEM) { + return UCS_ERR_NO_MEMORY; + } else if (RUNNING_ON_VALGRIND && (errno == EINVAL)) { + return UCS_ERR_NO_MEMORY; + } else { + ucs_error("shmat(shmid=%d) returned unexpected error: %m", *shmid); + return UCS_ERR_SHMEM_SEGMENT; + } + } + + *address_p = ptr; + return UCS_OK; +} + +void ucs_sysv_free(void *address) +{ + int ret; + + ret = shmdt(address); + + if (ret) { + ucs_warn("Unable to detach shared memory segment at %p: %m", address); + } +} + +unsigned ucs_get_mem_prot(void *address, size_t length) +{ + static int maps_fd = -1; + char buffer[1024]; + unsigned long start_addr, end_addr; + unsigned prot_flags; + char read_c, write_c, exec_c, priv_c; + char *ptr, *newline; + ssize_t read_size; + size_t read_offset; + int ret; + + if (maps_fd == -1) { + maps_fd = open(UCS_PROCESS_MAPS_FILE, O_RDONLY); + if (maps_fd < 0) { + ucs_fatal("cannot open %s for reading: %m", UCS_PROCESS_MAPS_FILE); + } + } + + ret = lseek(maps_fd, 0, SEEK_SET); + if (ret < 0) { + ucs_fatal("failed to lseek(0): %m"); + } + + prot_flags = PROT_READ|PROT_WRITE|PROT_EXEC; + + read_offset = 0; + while (1) { + read_size = read(maps_fd, buffer + read_offset, sizeof(buffer) - 1 - read_offset); + if (read_size < 0) { + if (errno == EINTR) { + continue; + } else { + ucs_fatal("failed to read from %s: %m", UCS_PROCESS_MAPS_FILE); + } + } else if (read_size == 0) { + goto out; + } else { + buffer[read_size + read_offset] = 0; + } + + ptr = buffer; + while ( (newline = strchr(ptr, '\n')) != NULL ) { + /* 00400000-0040b000 r-xp ... \n */ + ret = sscanf(ptr, "%lx-%lx %c%c%c%c", &start_addr, &end_addr, &read_c, + &write_c, &exec_c, &priv_c); + if (ret != 6) { + ucs_fatal("Parse error at %s", ptr); + } + + /* Address will not appear on the list */ + if ((uintptr_t)address < start_addr) { + goto out; + } + + /* Start address falls within current VMA */ + if ((uintptr_t)address < end_addr) { + ucs_trace_data("existing mapping: start=0x%08lx end=0x%08lx prot=%u", + start_addr, end_addr, prot_flags); + + if (read_c != 'r') { + prot_flags &= ~PROT_READ; + } + if (write_c != 'w') { + prot_flags &= ~PROT_WRITE; + } + if (exec_c != 'x') { + prot_flags &= ~PROT_EXEC; + } + + /* Finished going over entire memory region */ + if ((uintptr_t)(address + length) <= end_addr) { + return prot_flags; + } + + /* Start from the end of current VMA */ + address = (void*)end_addr; + } + + ptr = newline + 1; + } + + read_offset = strlen(ptr); + memmove(buffer, ptr, read_offset); + } +out: + return PROT_NONE; +} + +const char* ucs_get_process_cmdline() +{ + static char cmdline[1024] = {0}; + static int initialized = 0; + ssize_t len; + int i; + + if (!initialized) { + len = ucs_read_file(cmdline, sizeof(cmdline), 1, "/proc/self/cmdline"); + for (i = 0; i < len; ++i) { + if (cmdline[i] == '\0') { + cmdline[i] = ' '; + } + } + initialized = 1; + } + return cmdline; +} + +ucs_status_t ucs_sys_fcntl_modfl(int fd, int add, int remove) +{ + int oldfl, ret; + + oldfl = fcntl(fd, F_GETFL); + if (oldfl < 0) { + ucs_error("fcntl(fd=%d, F_GETFL) returned %d: %m", fd, oldfl); + return UCS_ERR_IO_ERROR; + } + + ret = fcntl(fd, F_SETFL, (oldfl | add) & ~remove); + if (ret < 0) { + ucs_error("fcntl(fd=%d, F_SETFL) returned %d: %m", fd, ret); + return UCS_ERR_IO_ERROR; + } + + return UCS_OK; +} + +ucs_cpu_model_t ucs_get_cpu_model() +{ +#ifdef __x86_64__ + unsigned _eax, _ebx, _ecx, _edx; + unsigned model, family; + unsigned ext_model, ext_family; + + /* Get CPU model/family */ + ucs_cpuid(0x1, &_eax, &_ebx, &_ecx, &_edx); + + model = (_eax >> 4) & UCS_MASK(8 - 4 ); + family = (_eax >> 8) & UCS_MASK(12 - 8 ); + ext_model = (_eax >> 16) & UCS_MASK(20 - 16); + ext_family = (_eax >> 20) & UCS_MASK(28 - 20); + + /* Adjust family/model */ + if (family == 0xf) { + family += ext_family; + } + if (family == 0x6 || family == 0xf) { + model = (ext_model << 4) | model; + } + + /* Check known CPUs */ + if (family == 0x06) { + switch (model) { + case 0x3a: + case 0x3e: + return UCS_CPU_MODEL_INTEL_IVYBRIDGE; + case 0x2a: + case 0x2d: + return UCS_CPU_MODEL_INTEL_SANDYBRIDGE; + case 0x1a: + case 0x1e: + case 0x1f: + case 0x2e: + return UCS_CPU_MODEL_INTEL_NEHALEM; + case 0x25: + case 0x2c: + case 0x2f: + return UCS_CPU_MODEL_INTEL_WESTMERE; + } + } + + return UCS_CPU_MODEL_UNKNOWN; + +#else + return UCS_CPU_MODEL_UNKNOWN; +#endif +} + +pid_t ucs_get_tid(void) +{ + return syscall(SYS_gettid); +} + +double ucs_get_cpuinfo_clock_freq(const char *mhz_header) +{ + double mhz = 0.0; + double m; + int rc; + FILE* f; + char buf[256]; + char fmt[256]; + int warn; + + f = fopen("/proc/cpuinfo","r"); + if (!f) { + return 0.0; + } + + snprintf(fmt, sizeof(fmt), "%s : %%lf", mhz_header); + + warn = 0; + while (fgets(buf, sizeof(buf), f)) { + + rc = sscanf(buf, fmt, &m); + if (rc != 1) { + continue; + } + + if (mhz == 0.0) { + mhz = m; + continue; + } + + if (mhz != m) { + mhz = ucs_max(mhz,m); + warn = 1; + } + } + fclose(f); + + if (warn) { + ucs_warn("Conflicting CPU frequencies detected, using: %.2f", mhz); + } + return mhz * 1e6; +} + +void ucs_empty_function() +{ +} diff --git a/src/ucs/sys/sys.h b/src/ucs/sys/sys.h new file mode 100644 index 00000000000..b82989157e4 --- /dev/null +++ b/src/ucs/sys/sys.h @@ -0,0 +1,280 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_SYS_H +#define UCS_SYS_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Valgrind support + */ +#ifndef NVALGRIND +# include +# ifndef VALGRIND_MAKE_MEM_DEFINED +# define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE(p, n) +# endif +# ifndef VALGRIND_MAKE_MEM_UNDEFINED +# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE(p, n) +# endif +#else +# define VALGRIND_MAKE_MEM_DEFINED(p, n) +# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) +# define VALGRIND_MAKE_MEM_NOACCESS(p, n) +# define VALGRIND_CREATE_MEMPOOL(n,p,x) +# define VALGRIND_DESTROY_MEMPOOL(p) +# define VALGRIND_MEMPOOL_ALLOC(n,p,x) +# define VALGRIND_MEMPOOL_FREE(n,p) +# define VALGRIND_COUNT_ERRORS 0 +# define VALGRIND_COUNT_LEAKS(a,b,c,d) { a = b = c = d = 0; } +# define RUNNING_ON_VALGRIND 0 +# define VALGRIND_PRINTF(...) +#endif + + +/* + * BullsEye Code Coverage tool + */ +#if _BullseyeCoverage +#define BULLSEYE_ON 1 +#define BULLSEYE_EXCLUDE_START #pragma BullseyeCoverage off +#define BULLSEYE_EXCLUDE_END #pragma BullseyeCoverage on +#define BULLSEYE_EXCLUDE_BLOCK_START "BullseyeCoverage save off"; +#define BULLSEYE_EXCLUDE_BLOCK_END "BullseyeCoverage restore"; +#else +#define BULLSEYE_ON 0 +#define BULLSEYE_EXCLUDE_START +#define BULLSEYE_EXCLUDE_END +#define BULLSEYE_EXCLUDE_BLOCK_START +#define BULLSEYE_EXCLUDE_BLOCK_END +#endif + + +/** + * @return Host name. + */ +const char *ucs_get_host_name(); + + +/** + * Expand a partial path to full path. + * + * @param path Path to expand. + * @param fullpath Filled with full path. + * @param max Room in "fullpath" + */ +void ucs_expand_path(const char *path, char *fullpath, size_t max); + + +/** + * @return Path to the main executable. + */ +const char *ucs_get_exe(); + + +/** + * Calculate checksum of a file. + */ +uint32_t ucs_file_checksum(const char *filename); + + +/** + * Get a globally unique identifier of the machine running the current process. + */ +uint64_t ucs_machine_guid(); + + +/** + * Get the first processor number we are bound to. + */ +int ucs_get_first_cpu(); + + +/** + * Generate a world-wide unique ID + * + * @param seed Additional seed to mix in. + * + * @note All bits of the returned number have the same randomness. + */ +uint64_t ucs_generate_uuid(uint64_t seed); + + +/** + * Fill a filename template. The following values in the string are replaced: + * %p - replaced by process id + * %h - replaced by host name + * + * @param tmpl File name template (possibly containing formatting sequences) + * @param buf Filled with resulting file name + * @param max Maximal size of destination buffer. + */ +void ucs_fill_filename_template(const char *tmpl, char *buf, size_t max); + + +/** + * Open an output stream according to user configuration: + * - file: - file name, %p, %h, %c are substituted. + * - stdout + * - stderr + * + * *p_fstream is filled with the stream handle, *p_need_close is set to whether + * fclose() should be called to release resources, *p_next_token to the remainder + * of config_str. + */ +ucs_status_t +ucs_open_output_stream(const char *config_str, FILE **p_fstream, int *p_need_close, + const char **p_next_token); + + +/** + * Return a number filled with the first characters of the string. + */ +uint64_t ucs_string_to_id(const char *str); + + +/** + * Read file contents into a string. If the size of the data is smaller than the + * supplied upper limit (max), a null terminator is appended to the data. + * + * @param buffer Buffer to fill with file contents. + * @param max Maximal buffer size. + * @param filename_fmt File name printf-like format string. + * + * @return Number of ytes read, or -1 in case of error. + */ +ssize_t ucs_read_file(char *buffer, size_t max, int silent, + const char *filename_fmt, ...) + UCS_F_PRINTF(4, 5); + + +/** + * @return Regular page size on the system. + */ +size_t ucs_get_page_size(); + + +/** + * @return Huge page size on the system. + */ +size_t ucs_get_huge_page_size(); + + +/** + * Allocate shared memory using SystemV API. + * + * @param size Pointer to memory size to allocate, updated with actual size + * (rounded up to huge page size or to regular page size). + * @param address_p Filled with allocated memory address. + * @param flags Flags to indicate the permissions for the allocate memory. + * (also, whether or not to allocate memory with huge pages). + * @param shmid Filled with the shmid from the shmget call in the function. + */ +ucs_status_t ucs_sysv_alloc(size_t *size, void **address_p, int flags, int *shimd); + + +/** + * Release memory allocated via hugetlb. + * + * @param address Memory to release (retuned from ucs_hugetlb_alloc). + */ +void ucs_sysv_free(void *address); + + +/** + * Retrieve memory access flags for a given region of memory. + * If the specified memory region has multiple different access flags, the AND + * of them is returned. If any part of the region is not mapped, PROT_NONE will + * be returned. + * + * @param address Region start. + * @param length Region length. + * @return Memory protection flags (PROT_xxx). + */ +unsigned ucs_get_mem_prot(void *address, size_t length); + + +/** + * Modify file descriptor flags via fcntl(). + * + * @param fd File descriptor to modify. + * @param add Flags to add. + * @param remove Flags to remove. + * + * Note: if a flags is specified in both add and remove, it will be removed. + */ +ucs_status_t ucs_sys_fcntl_modfl(int fd, int add, int remove); + + +/** + * Get process command line + */ +const char* ucs_get_process_cmdline(); + + +/** + * Get model of currently running cpu. + */ +ucs_cpu_model_t ucs_get_cpu_model(); + + +/** + * Get current thread (LWP) id. + */ +pid_t ucs_get_tid(void); + + +/** + * Get CPU frequency from /proc/cpuinfo. Return value is clocks-per-second. + * + * @param mhz_header String in /proc/cpuinfo which precedes the clock speed number. + */ +double ucs_get_cpuinfo_clock_freq(const char *mhz_header); + + +/** + * Empty function which can be casted to a no-operation callback in various situations. + */ +void ucs_empty_function(); + + +#endif diff --git a/src/ucs/time/time.c b/src/ucs/time/time.c new file mode 100644 index 00000000000..9736bde507f --- /dev/null +++ b/src/ucs/time/time.c @@ -0,0 +1,49 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +#include +#include +#include +#include + + +#define UCS_SECONDS_PER_DAY (60.0 * 60.0 * 24.0) + + +double UCS_F_CTOR ucs_get_cpu_clocks_per_sec() +{ + static double clocks_per_sec = 0.0; + static int initialized = 0; + + if (!initialized) { + clocks_per_sec = ucs_arch_get_clocks_per_sec(); + initialized = 1; + } + return clocks_per_sec; +} + +/* + * Calculate how much we should shift the 64-bit time value, so that it will fit + * a 32-bit integer, and cover an interval of at least one day. + */ +unsigned UCS_F_CTOR ucs_get_short_time_shift() +{ + static unsigned time_shift = 0; + static int initialized = 0; + double ts; + + if (!initialized) { + ts = ucs_log2(UCS_SECONDS_PER_DAY * ucs_get_cpu_clocks_per_sec()) - 31.0; + ucs_assert(ts >= 0.0); + ucs_assert(ts < 32.0); + time_shift = (int)ts; + initialized = 1; + } + + return time_shift; +} diff --git a/src/ucs/time/time.h b/src/ucs/time/time.h new file mode 100644 index 00000000000..0dcea40c889 --- /dev/null +++ b/src/ucs/time/time.h @@ -0,0 +1,142 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_TIME_H +#define UCS_TIME_H + +#include +#include + + +/** + * UCS time units. + * These are not necessarily aligned with metric time units. + * MUST compare short time values with UCS_SHORT_TIME_CMP to handle wrap-around. + */ +typedef unsigned long long ucs_time_t; + +/** + * Short time type + * Used to represent short time intervals, and takes less memory. + */ +typedef uint32_t ucs_short_time_t; + +/** + * Compare short time values + */ +#define UCS_SHORT_TIME_CMP UCS_CIRCULAR_COMPARE32 + + +#define UCS_TIME_INFINITY ULLONG_MAX + +#define UCS_MSEC_PER_SEC 1000ull /* Milli */ +#define UCS_USEC_PER_SEC 1000000ul /* Micro */ +#define UCS_NSEC_PER_SEC 1000000000ul /* Nano */ + + +double ucs_get_cpu_clocks_per_sec(); +unsigned ucs_get_short_time_shift(); + + +/** + * @return The current time, in UCS time units. + */ +static inline ucs_time_t ucs_get_time() +{ + return (ucs_time_t)ucs_arch_read_hres_clock(); +} + +/** + * @return The clock value of a single second. + */ +static inline double ucs_time_sec_value() +{ + return ucs_get_cpu_clocks_per_sec(); +} + + +/** + * Convert seconds to UCS time units. + */ +static inline ucs_time_t ucs_time_from_sec(double sec) +{ + return sec * ucs_time_sec_value(); +} + +/** + * Convert seconds to UCS time units. + */ +static inline ucs_time_t ucs_time_from_msec(double msec) +{ + return ucs_time_from_sec(msec / UCS_MSEC_PER_SEC); +} + +/** + * Convert seconds to UCS time units. + */ +static inline ucs_time_t ucs_time_from_usec(double usec) +{ + return ucs_time_from_sec(usec / UCS_USEC_PER_SEC); +} + +/** + * Convert UCS time units to seconds. + */ +static inline double ucs_time_to_sec(ucs_time_t time) +{ + return time / ucs_time_sec_value(); +} + +/** + * Convert UCS time units to milliseconds. + */ +static inline double ucs_time_to_msec(ucs_time_t time) +{ + return ucs_time_to_sec(time) * UCS_MSEC_PER_SEC; +} + +/** + * Convert UCS time units to microseconds. + */ +static inline double ucs_time_to_usec(ucs_time_t time) +{ + return ucs_time_to_sec(time) * UCS_USEC_PER_SEC; +} + +/** + * Convert UCS time units to nanoseconds. + */ +static inline double ucs_time_to_nsec(ucs_time_t time) +{ + return ucs_time_to_sec(time) * UCS_NSEC_PER_SEC; +} + +/** + * Convert UCS time interval (small) to nanoseconds. + */ +static inline double ucs_time_interval_to_nsec(ucs_time_t time) +{ + return ucs_time_to_sec(time * UCS_NSEC_PER_SEC); +} + +/* Convert seconds to POSIX timeval */ +static inline void ucs_sec_to_timeval(double seconds, struct timeval *tv) +{ + int64_t usec = (int64_t)( (seconds * UCS_USEC_PER_SEC) + 0.5 ); + tv->tv_sec = usec / UCS_USEC_PER_SEC; + tv->tv_usec = usec % UCS_USEC_PER_SEC; +} + +/* Convert seconds to POSIX timespec */ +static inline void ucs_sec_to_timespec(double seconds, struct timespec *ts) +{ + int64_t nsec = (int64_t)( (seconds * UCS_NSEC_PER_SEC) + 0.5 ); + ts->tv_sec = nsec / UCS_NSEC_PER_SEC; + ts->tv_nsec = nsec % UCS_NSEC_PER_SEC; +} + +#endif diff --git a/src/ucs/time/timer_wheel.c b/src/ucs/time/timer_wheel.c new file mode 100644 index 00000000000..4d9c7e2a9a7 --- /dev/null +++ b/src/ucs/time/timer_wheel.c @@ -0,0 +1,92 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2012-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include + +#include +#include + + +ucs_status_t ucs_twheel_init(ucs_twheel_t *twheel, ucs_time_t resolution) +{ + unsigned i; + + twheel->res = ucs_roundup_pow2(resolution); + twheel->res_order = (unsigned) ucs_log2(twheel->res); + twheel->num_slots = 1024; + twheel->current = 0; + twheel->now = ucs_get_time(); + twheel->wheel = malloc(sizeof(*twheel->wheel) * twheel->num_slots); + + for (i = 0; i < twheel->num_slots; i++) { + ucs_list_head_init(&twheel->wheel[i]); + } + + ucs_debug("high res timer created log=%d resolution=%lf usec wanted: %lf usec", + twheel->res_order, ucs_time_to_usec(twheel->res), ucs_time_to_usec(resolution)); + return UCS_OK; +} + +void ucs_twheel_cleanup(ucs_twheel_t *twheel) +{ + free(twheel->wheel); +} + +ucs_status_t ucs_wtimer_init(ucs_wtimer_t *t, void (*func)(ucs_callback_t *cb)) +{ + t->cb.func = func; + t->is_active = 0; + return UCS_OK; +} + +void __ucs_wtimer_add(ucs_twheel_t *t, ucs_wtimer_t *timer, ucs_time_t delta) +{ + uint64_t slot; + + timer->is_active = 1; + slot = delta>>t->res_order; + if (ucs_unlikely(slot == 0)) { + /* nothing really wrong with adding timer to the current slot. However + * we want to guard against the case we spend to much time in hi res + * timer processing */ + ucs_fatal("Timer resolution is too low. Min resolution %lf usec, wanted %lf usec", + ucs_time_to_usec(t->res), ucs_time_to_usec(delta)); + } + ucs_assert(slot > 0); + + if (ucs_unlikely(slot >= t->num_slots)) { + slot = t->num_slots - 1; + } + + slot = (t->current + slot) % t->num_slots; + ucs_assert(slot != t->current); + + ucs_list_add_tail(&t->wheel[slot], &timer->list); +} + +void __ucs_twheel_sweep(ucs_twheel_t *t, ucs_time_t current_time) +{ + ucs_wtimer_t *timer; + uint64_t slot; + + slot = (current_time - t->now) >> t->res_order; + t->now = current_time; + + if (ucs_unlikely(slot >= t->num_slots)) { + slot = t->num_slots - 1; + } + + slot = (t->current + slot) % t->num_slots; + + for (; t->current != slot; t->current = (t->current+1) % t->num_slots) { + while (!ucs_list_is_empty(&t->wheel[t->current])) { + timer = ucs_list_extract_head(&t->wheel[t->current], ucs_wtimer_t, list); + timer->is_active = 0; + ucs_invoke_callback(&timer->cb); + } + } +} diff --git a/src/ucs/time/timer_wheel.h b/src/ucs/time/timer_wheel.h new file mode 100644 index 00000000000..07fe6ec772b --- /dev/null +++ b/src/ucs/time/timer_wheel.h @@ -0,0 +1,115 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2012-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_WHEEL_H +#define UCS_WHEEL_H + +#include +#include +#include +#include + + +/** + * UCS high resolution timer. + */ +typedef struct ucs_wtimer { + ucs_callback_t cb; /* User callback */ + ucs_list_link_t list; /* Link in the list of timers */ + int is_active; +} ucs_wtimer_t; + + +typedef struct ucs_timer_wheel { + ucs_time_t res; + ucs_time_t now; /* when wheel was last updated */ + uint64_t current; + ucs_list_link_t *wheel; + unsigned res_order; + unsigned num_slots; +} ucs_twheel_t; + + +/** + * Initialize wheel timer + * @param cb Callback to call + */ +ucs_status_t ucs_wtimer_init(ucs_wtimer_t *t, void (*func)(ucs_callback_t *cb)); + + +/** + * Initialize the timer queue. + * + * @param twheel Timer queue to initialize. + * @param resolution Timer resolution. Timer wheel range is from now to now + UCS_TWHEEL_NSLOTS * res + */ +ucs_status_t ucs_twheel_init(ucs_twheel_t *twheel, ucs_time_t resolution); + + +/** + * Cleanup the timer queue. + * + * @param twheel Timer queue to clean up. + */ +void ucs_twheel_cleanup(ucs_twheel_t *twheel); + + +/** + * Go through the timers in the timer queue, dispatch expired timers. + * + * @param twheel Timer wheel to dispatch timers on. + * @param current_time Current time to dispatch the timers for. + * + * @note Timers which expired between calls to this function will also be dispatched. + * @note There is no guarantee on the order of dispatching. + */ +void __ucs_twheel_sweep(ucs_twheel_t *t, ucs_time_t current_time); +static inline void ucs_twheel_sweep(ucs_twheel_t *t, ucs_time_t current_time) +{ + if (ucs_unlikely(current_time - t->now >= t->res)) { + __ucs_twheel_sweep(t, current_time); + } +} + + +/** + * Add a one shot timer. + * + * @param twheel Timer queue to schedule on. + * @param timer Timer callback to invoke every time. + * @param delta Invocation time + * + * NOTE: adding timer already in queue will do nothing + */ +void __ucs_wtimer_add(ucs_twheel_t *t, ucs_wtimer_t *timer, ucs_time_t delta); +static inline ucs_status_t ucs_wtimer_add(ucs_twheel_t *t, ucs_wtimer_t *timer, + ucs_time_t delta) +{ + if (ucs_likely(timer->is_active)) { + /* most of the times we try to schedule already active timer */ + return UCS_ERR_BUSY; + } + + __ucs_wtimer_add(t, timer, delta); + return UCS_OK; +} + + +/** + * Remove a timer. + * + * @param timer timer to remove. + */ +static inline void ucs_wtimer_remove(ucs_wtimer_t *timer) +{ + if (ucs_likely(timer->is_active)) { + ucs_list_del(&timer->list); + timer->is_active = 0; + } +} + +#endif diff --git a/src/ucs/time/timerq.c b/src/ucs/time/timerq.c new file mode 100644 index 00000000000..badff30cb4e --- /dev/null +++ b/src/ucs/time/timerq.c @@ -0,0 +1,84 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "timerq.h" + +#include +#include + +static void ucs_timer_reschedule(ucs_timer_queue_t *timerq, ucs_timer_t *timer) +{ + timerq->expiration = ucs_min(timerq->expiration, timer->expiration); +} + +ucs_status_t ucs_timerq_init(ucs_timer_queue_t *timerq) +{ + ucs_trace_func("timerq=%p", timerq); + + ucs_list_head_init(&timerq->timers); + timerq->expiration = UCS_TIME_INFINITY; + return UCS_OK; +} + +void ucs_timerq_cleanup(ucs_timer_queue_t *timerq) +{ + ucs_timer_t *timer; + + ucs_trace_func("timerq=%p", timerq); + + while (!ucs_list_is_empty(&timerq->timers)) { + timer = ucs_list_extract_head(&timerq->timers, ucs_timer_t, list); + ucs_warn("removing timer cb=%p", timer->cb); + ucs_free(timer); + } +} + +void ucs_timerq_sweep_internal(ucs_timer_queue_t *timerq, ucs_time_t current_time) +{ + ucs_timer_t *timer, *tmp; + + timerq->expiration = UCS_TIME_INFINITY; + ucs_list_for_each_safe(timer, tmp, &timerq->timers, list) { + if (current_time >= timer->expiration) { + ucs_invoke_callback(timer->cb); + timer->expiration = current_time + timer->interval; + ucs_timer_reschedule(timerq, timer); + } + timerq->expiration = ucs_min(timerq->expiration, timer->expiration); + } +} + +ucs_status_t ucs_timer_add(ucs_timer_queue_t *timerq, ucs_callback_t *callback, + ucs_time_t interval) +{ + ucs_timer_t *timer; + + timer = ucs_malloc(sizeof *timer, "timer"); + if (timer == NULL) { + ucs_error("failed to allocate timer"); + return UCS_ERR_NO_MEMORY; + } + + timer->cb = callback; + timer->interval = interval; + timer->expiration = 0; /* will fire the next time sweep is called */ + ucs_list_add_tail(&timerq->timers, &timer->list); + ucs_timer_reschedule(timerq, timer); + return UCS_OK; +} + +void ucs_timer_remove(ucs_timer_queue_t *timerq, ucs_callback_t *callback) +{ + ucs_timer_t *timer, *tmp; + + ucs_list_for_each_safe(timer, tmp, &timerq->timers, list) { + if (timer->cb == callback) { + ucs_list_del(&timer->list); + ucs_free(timer); + } + } +} diff --git a/src/ucs/time/timerq.h b/src/ucs/time/timerq.h new file mode 100644 index 00000000000..d8fa69f159d --- /dev/null +++ b/src/ucs/time/timerq.h @@ -0,0 +1,87 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_TIMERQ_H +#define UCS_TIMERQ_H + +#include +#include +#include + + +/** + * UCS timer. + */ +typedef struct ucs_timer { + ucs_callback_t *cb; /* User callback */ + ucs_time_t interval; /* Re-scheduling interval */ + ucs_time_t expiration; /* Absolute timer expiration time */ + ucs_list_link_t list; /* Link in the list of timers */ +} ucs_timer_t; + + +typedef struct ucs_timer_queue { + ucs_time_t expiration; /* Expiration of next timer */ + ucs_list_link_t timers; /* List of timers */ +} ucs_timer_queue_t; + + +/** + * Initialize the timer queue. + * + * @param timerq Timer queue to initialize. + */ +ucs_status_t ucs_timerq_init(ucs_timer_queue_t *timerq); + +/** + * Cleanup the timer queue. + * + * @param timerq Timer queue to clean up. + */ +void ucs_timerq_cleanup(ucs_timer_queue_t *timerq); + +void ucs_timerq_sweep_internal(ucs_timer_queue_t *timerq, ucs_time_t current_time); + +/** + * Go through the timers in the timer queue, dispatch expired timers, and reschedule + * periodic timers. + * + * @param timerq Timer queue to dispatch timers on. + * @param current_time Current time to dispatch the timers for. + * + * @note Timers which expired between calls to this function will also be dispatched. + * @note There is no guarantee on the order of dispatching. + */ +static inline void ucs_timerq_sweep(ucs_timer_queue_t *timerq, ucs_time_t current_time) +{ + if (current_time >= timerq->expiration) { + ucs_timerq_sweep_internal(timerq, current_time); + } +} + +/** + * Add a periodic timer. + * + * @param timerq Timer queue to schedule on. + * @param callback Callback to invoke every time. + * @param interval Timer interval. + * + * NOTE: The timer callback is allowed to remove itself from the queue by + * calling ucs_timer_remove(). + */ +ucs_status_t ucs_timer_add(ucs_timer_queue_t *timerq, ucs_callback_t *callback, + ucs_time_t interval); + +/** + * Remove a timer. + * + * @param timerq Time queue this timer was scheduled on. + * @param callback Callback to remove. + */ +void ucs_timer_remove(ucs_timer_queue_t *timerq, ucs_callback_t *callback); + +#endif diff --git a/src/ucs/type/callback.c b/src/ucs/type/callback.c new file mode 100644 index 00000000000..77e82e554b3 --- /dev/null +++ b/src/ucs/type/callback.c @@ -0,0 +1,86 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "callback.h" + +#include +#include + + +void ucs_notifier_chain_init(ucs_notifier_chain_t *chain) +{ + ucs_notifier_chain_elem_t *elem; + + for (elem = chain->elems; elem < chain->elems + UCS_NOTIFIER_CHAIN_MAX; ++elem) { + elem->func = NULL; + elem->arg = NULL; + elem->refcount = 0; + VALGRIND_MAKE_MEM_UNDEFINED(&elem->arg, sizeof(elem->arg)); + } +} + +int ucs_notifier_chain_add(ucs_notifier_chain_t *chain, ucs_notifier_chain_func_t func, void *arg) +{ + ucs_notifier_chain_elem_t *elem, *free_slot; + char func_name[200]; + + ucs_notifier_chain_for_each(elem, chain) { + if ((elem->func == func) && (elem->arg == arg)) { + ++elem->refcount; + return 0; + } + } + + free_slot = elem; + + if (free_slot - chain->elems >= UCS_NOTIFIER_CHAIN_MAX) { + ucs_fatal("overflow in progress chain while adding %s", + ucs_debug_get_symbol_name(func, func_name, sizeof(func_name))); + } + + ucs_debug("add %s to progress chain %p", + ucs_debug_get_symbol_name(func, func_name, sizeof(func_name)), + chain); + free_slot->func = func; + free_slot->arg = arg; + free_slot->refcount = 1; + return 1; +} + +int ucs_notifier_chain_remove(ucs_notifier_chain_t *chain, ucs_notifier_chain_func_t func, void *arg) +{ + ucs_notifier_chain_elem_t *elem, *removed_elem, *last_elem; + char func_name[200]; + + removed_elem = NULL; + last_elem = NULL; + ucs_notifier_chain_for_each(elem, chain) { + if (elem->func == func && elem->arg == arg) { + removed_elem = elem; + } + last_elem = elem; + } + + if (removed_elem == NULL) { + ucs_debug("callback not found in progress chain"); + return 0; + } + + --removed_elem->refcount; + if (removed_elem->refcount != 0) { + return 0; + } + + ucs_debug("remove %s from progress chain %p", + ucs_debug_get_symbol_name(func, func_name, sizeof(func_name)), + chain); + *removed_elem = *last_elem; + last_elem->func = NULL; + last_elem->arg = NULL; + last_elem->refcount = 0; + return 1; +} diff --git a/src/ucs/type/callback.h b/src/ucs/type/callback.h new file mode 100644 index 00000000000..9df6503db3b --- /dev/null +++ b/src/ucs/type/callback.h @@ -0,0 +1,78 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_CALLBACK_H +#define UCS_CALLBACK_H + +#include +#include +#include + +/* + * Forward declarations + */ +typedef struct ucs_callback ucs_callback_t; +typedef struct ucs_notifier_chain ucs_notifier_chain_t; +typedef struct ucs_notifier_chain_elem ucs_notifier_chain_elem_t; +typedef void (*ucs_notifier_chain_func_t)(void *arg); + +#define UCS_NOTIFIER_CHAIN_MAX 16 + +/** + * A generic callback which can be embedded into structures. + */ +struct ucs_callback { + void (*func)(ucs_callback_t *self); +}; + +struct ucs_notifier_chain_elem { + ucs_notifier_chain_func_t func; + void *arg; + unsigned refcount; +}; + +/** + * A list of callbacks. It's a circular single-linked list, and cursor points + * to one of the elements. + */ +struct ucs_notifier_chain { + ucs_notifier_chain_elem_t elems[UCS_NOTIFIER_CHAIN_MAX]; +}; + + +/** + * Iterate over all elements in the notifier chain + */ +#define ucs_notifier_chain_for_each(_elem, _chain) \ + for (_elem = &(_chain)->elems[0]; _elem->func != NULL; ++_elem) + + +static inline void ucs_invoke_callback(ucs_callback_t *cb) +{ + cb->func(cb); +} + +void ucs_notifier_chain_init(ucs_notifier_chain_t *chain); + +static inline void ucs_notifier_chain_call(ucs_notifier_chain_t *chain) +{ + ucs_notifier_chain_elem_t *elem; + + ucs_notifier_chain_for_each(elem, chain) { + elem->func(elem->arg); + } +} + + +/* @return whether added a new entry */ +int ucs_notifier_chain_add(ucs_notifier_chain_t *chain, ucs_notifier_chain_func_t func, void *arg); + +/* @return whether removed an entry */ +int ucs_notifier_chain_remove(ucs_notifier_chain_t *chain, ucs_notifier_chain_func_t func, void *arg); + + +#endif diff --git a/src/ucs/type/spinlock.h b/src/ucs/type/spinlock.h new file mode 100644 index 00000000000..d5c92ff9d19 --- /dev/null +++ b/src/ucs/type/spinlock.h @@ -0,0 +1,106 @@ +/* +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#ifndef UCS_SPINLOCK_H +#define UCS_SPINLOCK_H + +#include +#include + +/** + * Reentrant spinlock. + */ +typedef struct ucs_spinlock { + pthread_spinlock_t lock; + int count; + pthread_t owner; +#if ENABLE_DEBUG_DATA + const char * file; + int line; +#endif +} ucs_spinlock_t; + + +static inline ucs_status_t ucs_spinlock_init(ucs_spinlock_t *lock) +{ + int ret; + + ret = pthread_spin_init(&lock->lock, 0); + if (ret != 0) { + ucs_error("pthread_spin_init() returned %d: %m", ret); + return UCS_ERR_IO_ERROR; + } + + lock->count = 0; + lock->owner = 0xfffffffful; +#if ENABLE_DEBUG_DATA + lock->file = ""; + lock->line = 0; +#endif + return UCS_OK; +} + +static inline int ucs_spin_is_owner(ucs_spinlock_t *lock, pthread_t self) +{ + return lock->owner == self; +} + +static inline void __ucs_spin_lock(ucs_spinlock_t *lock, const char *file, int line) +{ + pthread_t self = pthread_self(); + + if (ucs_spin_is_owner(lock, self)) { + ++lock->count; + return; + } + + pthread_spin_lock(&lock->lock); + ucs_assertv(lock->count == 0, "count=%d owner=0x%lx", lock->count, lock->owner); + lock->owner = self; + ++lock->count; +#if ENABLE_DEBUG_DATA + lock->file = file; + lock->line = line; +#endif +} +#define ucs_spin_lock(_lock) __ucs_spin_lock(_lock, __FILE__, __LINE__) + +static inline int __ucs_spin_trylock(ucs_spinlock_t *lock, const char *file, int line) +{ + pthread_t self = pthread_self(); + + if (ucs_spin_is_owner(lock, self)) { + ++lock->count; + return 1; + } + + if (pthread_spin_trylock(&lock->lock) != 0) { + return 0; + } + + lock->owner = self; + ++lock->count; +#if ENABLE_DEBUG_DATA + lock->file = file; + lock->line = line; +#endif + return 1; +} +#define ucs_spin_trylock(_lock) __ucs_spin_trylock(_lock, __FILE__, __LINE__) + +static inline void ucs_spin_unlock(ucs_spinlock_t *lock) +{ + ucs_assert(ucs_spin_is_owner(lock, pthread_self())); + + --lock->count; + if (lock->count == 0) { + lock->owner = 0xfffffffful; + pthread_spin_unlock(&lock->lock); + } +} + +#endif diff --git a/src/ucs/type/status.c b/src/ucs/type/status.c new file mode 100644 index 00000000000..744105c6876 --- /dev/null +++ b/src/ucs/type/status.c @@ -0,0 +1,65 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include "status.h" + + +const char *ucs_status_string(ucs_status_t status) +{ + switch (status) { + case UCS_OK: + return "Success"; + case UCS_INPROGRESS: + return "Operation in progress"; + case UCS_ERR_NO_MESSAGE: + return "No pending message"; + case UCS_ERR_WOULD_BLOCK: + return "Operation cannot be completed without blocking"; + case UCS_ERR_IO_ERROR: + return "Input/output error"; + case UCS_ERR_NO_MEMORY: + return "Out of memory"; + case UCS_ERR_INVALID_PARAM: + return "Invalid parameter"; + case UCS_ERR_UNREACHABLE: + return "Destination is unreachable"; + case UCS_ERR_INVALID_ADDR: + return "Address not valid"; + case UCS_ERR_NOT_IMPLEMENTED: + return "Function not implemented"; + case UCS_ERR_MESSAGE_TRUNCATED: + return "Message truncated"; + case UCS_ERR_NO_PROGRESS: + return "No progress"; + case UCS_ERR_BUFFER_TOO_SMALL: + return "Provided buffer is too small"; + case UCS_ERR_NO_ELEM: + return "No such element"; + case UCS_ERR_SOME_CONNECTS_FAILED: + return "Failed to connect some of the requested endpoints"; + case UCS_ERR_NO_DEVICE: + return "No such device"; + case UCS_ERR_BUSY: + return "Device is busy"; + case UCS_ERR_CANCELED: + return "Request canceled"; + case UCS_ERR_SHMEM_SEGMENT: + return "Shared memory error"; + case UCS_ERR_ALREADY_EXISTS: + return "Element already exists"; + case UCS_ERR_OUT_OF_RANGE: + return "Index out of range"; + case UCS_ERR_TIMED_OUT: + return "Operation timed out"; + case UCS_ERR_EXCEEDS_LIMIT: + return "User-defined limit was reached"; + case UCS_ERR_UNSUPPORTED: + return "Unsupported operation"; + default: + return "Unknown error"; + }; +} diff --git a/src/ucs/type/status.h b/src/ucs/type/status.h index 45b7c165904..1614194adb7 100644 --- a/src/ucs/type/status.h +++ b/src/ucs/type/status.h @@ -13,10 +13,45 @@ * Status codes */ typedef enum { - UCS_SUCCESS = 0, - UCS_ERR_INPROGRESS = 1, - UCS_ERR_INVALID_PARAM = -1 + /* Operation completed successfully */ + UCS_OK = 0, + + /* Operation is queued and still in progress */ + UCS_INPROGRESS = 1, + + /* Failure codes */ + UCS_ERR_NO_MESSAGE = -1, + UCS_ERR_WOULD_BLOCK = -2, + UCS_ERR_IO_ERROR = -3, + UCS_ERR_NO_MEMORY = -4, + UCS_ERR_INVALID_PARAM = -5, + UCS_ERR_UNREACHABLE = -6, + UCS_ERR_INVALID_ADDR = -7, + UCS_ERR_NOT_IMPLEMENTED = -8, + UCS_ERR_MESSAGE_TRUNCATED = -9, + UCS_ERR_NO_PROGRESS = -10, + UCS_ERR_BUFFER_TOO_SMALL = -11, + UCS_ERR_NO_ELEM = -12, + UCS_ERR_SOME_CONNECTS_FAILED = -13, + UCS_ERR_NO_DEVICE = -14, + UCS_ERR_BUSY = -15, + UCS_ERR_CANCELED = -16, + UCS_ERR_SHMEM_SEGMENT = -17, + UCS_ERR_ALREADY_EXISTS = -18, + UCS_ERR_OUT_OF_RANGE = -19, + UCS_ERR_TIMED_OUT = -20, + UCS_ERR_EXCEEDS_LIMIT = -21, + UCS_ERR_UNSUPPORTED = -22, + UCS_ERR_LAST } ucs_status_t; +/** + * @param status UCS status code. + * + * @return Verbose status message. + */ +const char *ucs_status_string(ucs_status_t status); + + #endif diff --git a/src/uct/api/tl.h b/src/uct/api/tl.h index 031515de72a..e6290bb4f86 100644 --- a/src/uct/api/tl.h +++ b/src/uct/api/tl.h @@ -61,9 +61,9 @@ struct uct_ops { void (*iface_close)(uct_iface_h iface); ucs_status_t (*iface_query)(uct_iface_h iface, - uct_iface_attr_t *iface_attr); + uct_iface_attr_t *iface_attr); ucs_status_t (*iface_get_address)(uct_iface_h iface, - uct_iface_addr_t *iface_addr); + uct_iface_addr_t *iface_addr); ucs_status_t (*ep_create)(uct_ep_h *ep_p); void (*ep_destroy)(uct_ep_h ep); diff --git a/src/uct/tl/tl.c b/src/uct/tl/tl.c index ae26050ecee..8d95fb88505 100644 --- a/src/uct/tl/tl.c +++ b/src/uct/tl/tl.c @@ -10,7 +10,7 @@ ucs_status_t uct_init(uct_context_h *context_p) { - return UCS_SUCCESS; + return UCS_OK; } void uct_cleanup(uct_context_h context) diff --git a/test/gtest/Makefile.am b/test/gtest/Makefile.am new file mode 100644 index 00000000000..43292b93ef2 --- /dev/null +++ b/test/gtest/Makefile.am @@ -0,0 +1,77 @@ +# +# Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +# +# $COPYRIGHT$ +# $HEADER$ +# + +UCS_HANDLE_ERRORS ?= freeze +export UCS_HANDLE_ERRORS + +GTEST_FILTER ?= * +export GTEST_FILTER + +VALGRIND_EXTRA_ARGS = --xml=yes --xml-file=valgrind.xml + +VALGRIND_ARGS = \ + --tool=memcheck \ + --leak-check=full \ + --track-origins=yes \ + --fair-sched=try + +noinst_PROGRAMS = gtest + +gtestdir = $(includedir) +gtest_LDADD = \ + $(abs_top_builddir)/src/ucs/libucs.la \ + $(abs_top_builddir)/src/uct/libuct.la \ + $(abs_top_builddir)/src/ucs/libucstest.la \ + $(GTEST_LIBS) \ + $(BOOST_THREAD_LIB) + +gtest_CPPFLAGS = \ + -I$(abs_top_srcdir)/src \ + -I$(abs_top_builddir)/src \ + -I$(abs_top_srcdir)/test/gtest \ + $(GTEST_CPPFLAGS) + +gtest_LDFLAGS = $(GTEST_LDFLAGS) -no-install +gtest_CXXFLAGS = $(GTEST_CXXFLAGS) -g -O3 + +gtest_SOURCES = \ + ucs/test_config.cc \ + ucs/test_datatype.cc \ + ucs/test_debug.cc \ + ucs/test_memtrack.cc \ + ucs/test_instr.cc \ + ucs/test_math.cc \ + ucs/test_mpool.cc \ + ucs/test_stats.cc \ + ucs/test_sys.cc \ + ucs/test_timer.cc \ + ucs/test_twheel.cc \ + ucs/test_frag_list.cc + +.PHONY: test test gdb valgrind fix_rpath + +all-local: gtest + +test: gtest + @rm -f core.* + $(abs_builddir)/gtest --gtest_filter=$(GTEST_FILTER) + +test_gdb: + echo 'r' > .gdbcommands + env UCS_HANDLE_ERRORS=none UCS_GDB_PATH="" gdb -x .gdbcommands --args $(GDB_ARGS) $(abs_builddir)/gtest --gtest_filter=$(GTEST_FILTER) + +list: gtest + $(abs_builddir)/gtest --gtest_list_tests --gtest_filter=$(GTEST_FILTER) + +test_valgrind: gtest + . /usr/share/Modules/init/sh && \ + module load dev/mofed_valgrind && \ + valgrind $(VALGRIND_ARGS) $(abs_builddir)/gtest --gtest_filter=$(GTEST_FILTER) + +test_jenkins_valgrind: gtest + valgrind $(VALGRIND_ARGS) $(VALGRIND_EXTRA_ARGS) $(abs_builddir)/gtest --gtest_filter=$(GTEST_FILTER) + diff --git a/test/gtest/ucs/test_config.cc b/test/gtest/ucs/test_config.cc new file mode 100644 index 00000000000..50d9d855bd1 --- /dev/null +++ b/test/gtest/ucs/test_config.cc @@ -0,0 +1,256 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +#include +} + +#include +#include +#include +#include + + +typedef enum { + COLOR_RED, + COLOR_BLUE, + COLOR_BLACK, + COLOR_YELLOW, + COLOR_WHITE, + COLOR_LAST +} color_t; + +const char *color_names[] = { + /*[COLOR_RED] =*/ "red", + /*[COLOR_BLUE] =*/ "blue", + /*[COLOR_BLACK] =*/ "black", + /*[COLOR_YELLOW] =*/ "yellow", + /*[COLOR_WHITE] =*/ "white", + /*[COLOR_LAST] =*/ NULL +}; + +typedef struct { + unsigned color; +} seat_opts_t; + +typedef struct { + seat_opts_t driver_seat; + seat_opts_t passenger_seat; + seat_opts_t rear_seat; +} coach_opts_t; + +typedef struct { + unsigned volume; +} engine_opts_t; + +typedef struct { + engine_opts_t engine; + coach_opts_t coach; + unsigned price; + const char *brand; + const char *model; + unsigned color; +} car_opts_t; + + +ucs_config_field_t seat_opts_table[] = { + {"COLOR", "black", "Seat color", + ucs_offsetof(seat_opts_t, color), UCS_CONFIG_TYPE_ENUM(color_names)}, + + {NULL} +}; + +ucs_config_field_t coach_opts_table[] = { + {"DRIVER_", "COLOR=red", "Driver seat options", + ucs_offsetof(coach_opts_t, driver_seat), UCS_CONFIG_TYPE_TABLE(seat_opts_table)}, + + {"PASSENGER_", "", "Passenger seat options", + ucs_offsetof(coach_opts_t, passenger_seat), UCS_CONFIG_TYPE_TABLE(seat_opts_table)}, + + {"REAR_", "", "Rear seat options", + ucs_offsetof(coach_opts_t, rear_seat), UCS_CONFIG_TYPE_TABLE(seat_opts_table)}, + + {NULL} +}; + +ucs_config_field_t engine_opts_table[] = { + {"VOLUME", "6000", "Engine volume", + ucs_offsetof(engine_opts_t, volume), UCS_CONFIG_TYPE_UINT}, + + {NULL} +}; + +ucs_config_field_t car_opts_table[] = { + {"ENGINE_", "", "Engine options", + ucs_offsetof(car_opts_t, engine), UCS_CONFIG_TYPE_TABLE(engine_opts_table)}, + + {"COACH_", "PASSENGER_COLOR=blue", "Seats options", + ucs_offsetof(car_opts_t, coach), UCS_CONFIG_TYPE_TABLE(coach_opts_table)}, + + {"PRICE", "999", "Price", + ucs_offsetof(car_opts_t, price), UCS_CONFIG_TYPE_UINT}, + + {"BRAND", "Chevy", "Car brand", + ucs_offsetof(car_opts_t, brand), UCS_CONFIG_TYPE_STRING}, + + {"MODEL", "Corvette", "Car model", + ucs_offsetof(car_opts_t, model), UCS_CONFIG_TYPE_STRING}, + + {"COLOR", "red", "Car color", + ucs_offsetof(car_opts_t, color), UCS_CONFIG_TYPE_ENUM(color_names)}, + + {NULL} +}; + +class test_config : public ucs::test { +protected: + + /* + * Wrapper class for car options parser. + */ + class car_opts { + public: + car_opts(const char *user_prefix = NULL) : m_opts(parse(user_prefix)) { + } + + car_opts(const car_opts& orig) : + m_opts((car_opts_t*)ucs_malloc(sizeof *m_opts, "opts")) + { + ucs_status_t status = ucs_config_parser_clone_opts(orig.m_opts, + m_opts, + car_opts_table); + ASSERT_UCS_OK(status); + } + + ~car_opts() { + ucs_config_parser_release_opts(m_opts, car_opts_table); + ucs_free(m_opts); + } + + void set(const char *name, const char *value) { + ucs_config_parser_set_value(m_opts, car_opts_table, name, value); + } + + car_opts_t* operator->() const { + return m_opts; + } + + car_opts_t* operator*() const { + return m_opts; + } + private: + + static car_opts_t *parse(const char *user_prefix) { + car_opts_t *tmp; + ucs_status_t status = ucs_config_parser_read_opts(car_opts_table, + user_prefix, + sizeof(*tmp), + (void**)&tmp); + ASSERT_UCS_OK(status); + return tmp; + } + + car_opts_t * const m_opts; + }; +}; + +UCS_TEST_F(test_config, parse_default) { + car_opts opts("TEST"); + + EXPECT_EQ(999, opts->price); + EXPECT_EQ(std::string("Chevy"), opts->brand); + EXPECT_EQ(std::string("Corvette"), opts->model); + EXPECT_EQ(COLOR_RED, opts->color); + EXPECT_EQ(6000, opts->engine.volume); + EXPECT_EQ(COLOR_RED, opts->coach.driver_seat.color); + EXPECT_EQ(COLOR_BLUE, opts->coach.passenger_seat.color); + EXPECT_EQ(COLOR_BLACK, opts->coach.rear_seat.color); + +} + +UCS_TEST_F(test_config, parse_with_prefix) { + ucs::scoped_setenv env1("UCS_COLOR", "white"); + ucs::scoped_setenv env2("UCS_TEST_COLOR", "black"); + ucs::scoped_setenv env3("UCS_DRIVER_COLOR", "yellow"); + ucs::scoped_setenv env4("UCS_TEST_REAR_COLOR", "white"); + + car_opts dfl, test("TEST"); + EXPECT_EQ(COLOR_WHITE, dfl->color); + EXPECT_EQ(COLOR_BLACK, test->color); + EXPECT_EQ(COLOR_YELLOW, test->coach.driver_seat.color); + EXPECT_EQ(COLOR_WHITE, test->coach.rear_seat.color); +} + +UCS_TEST_F(test_config, clone) { + + boost::shared_ptr opts_clone_ptr; + + { + ucs::scoped_setenv env1("UCS_TEST_COLOR", "white"); + car_opts opts("TEST"); + EXPECT_EQ(COLOR_WHITE, opts->color); + + ucs::scoped_setenv env2("UCS_TEST_COLOR", "black"); + opts_clone_ptr = boost::make_shared(opts); + } + + EXPECT_EQ(COLOR_WHITE, (*opts_clone_ptr)->color); +} + +UCS_TEST_F(test_config, set) { + car_opts opts("TEST"); + EXPECT_EQ(COLOR_RED, opts->color); + + opts.set("COLOR", "white"); + EXPECT_EQ(COLOR_WHITE, opts->color); + +} + +UCS_TEST_F(test_config, performance) { + + /* Add stuff to env to presumably make getenv() slower */ + boost::ptr_vector env; + for (unsigned i = 0; i < 300; ++i) { + env.push_back(new ucs::scoped_setenv( + (std::string("MTEST") + boost::lexical_cast(i)).c_str(), + "")); + } + + /* Now test the time */ + UCS_TEST_TIME_LIMIT(0.005) { + car_opts opts("TEST"); + } +} + +UCS_TEST_F(test_config, dump) { + char *dump_data; + size_t dump_size; + char line_buf[1024]; + + car_opts opts("TEST"); + + /* Dump configuration to a memory buffer */ + dump_data = NULL; + FILE *file = open_memstream(&dump_data, &dump_size); + ucs_config_parser_print_opts(file, "", *opts, car_opts_table, + 0); + + /* Sanity check - all lines begin with UCS_ */ + unsigned num_lines = 0; + fseek(file, 0, SEEK_SET); + while (fgets(line_buf, sizeof(line_buf), file)) { + line_buf[4] = '\0'; + EXPECT_STREQ("UCS_", line_buf); + ++num_lines; + } + EXPECT_EQ(8u, num_lines); + + fclose(file); + free(dump_data); +} diff --git a/test/gtest/ucs/test_datatype.cc b/test/gtest/ucs/test_datatype.cc new file mode 100644 index 00000000000..28e5c191033 --- /dev/null +++ b/test/gtest/ucs/test_datatype.cc @@ -0,0 +1,521 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +#include +#include +#include +#include +} + +#include +#include +#include +#include + +class test_datatype : public ucs::test { +}; + +typedef struct { + int i; + ucs_list_link_t list; + ucs_queue_elem_t queue; +} elem_t; + +UCS_TEST_F(test_datatype, list_basic) { + + ucs_list_link_t head; + elem_t elem0, elem1; + elem_t *iter, *tmp; + + ucs_list_head_init(&head); + ASSERT_EQ(0, ucs_list_length(&head)); + ucs_list_insert_after(&head, &elem0.list); + ucs_list_insert_before(&head, &elem1.list); + + std::vector vec; + ucs_list_for_each(iter, &head, list) { + vec.push_back(iter); + } + ASSERT_EQ(2ul, vec.size()); + ASSERT_EQ(&elem0, vec[0]); + ASSERT_EQ(&elem1, vec[1]); + ASSERT_EQ(2, ucs_list_length(&head)); + + ucs_list_for_each_safe(iter, tmp, &head, list) { + ucs_list_del(&iter->list); + } + ASSERT_TRUE(ucs_list_is_empty(&head)); + ASSERT_EQ(0, ucs_list_length(&head)); +} + +UCS_TEST_F(test_datatype, list_splice) { + + ucs_list_link_t head1, head2; + elem_t l1_elem0, l1_elem1, l1_elem2; + elem_t l2_elem0, l2_elem1, l2_elem2; + elem_t *iter; + + ucs_list_head_init(&head1); + ucs_list_head_init(&head2); + + l1_elem0.i = 0; + ucs_list_add_tail(&head1, &l1_elem0.list); + l1_elem1.i = 1; + ucs_list_add_tail(&head1, &l1_elem1.list); + l1_elem2.i = 2; + ucs_list_add_tail(&head1, &l1_elem2.list); + + l2_elem0.i = 3; + ucs_list_add_tail(&head2, &l2_elem0.list); + l2_elem1.i = 4; + ucs_list_add_tail(&head2, &l2_elem1.list); + l2_elem2.i = 5; + ucs_list_add_tail(&head2, &l2_elem2.list); + + ucs_list_splice_tail(&head1, &head2); + + int i = 0; + ucs_list_for_each(iter, &head1, list) { + EXPECT_EQ(i, iter->i); + ++i; + } +} + +UCS_TEST_F(test_datatype, queue) { + + ucs_queue_head_t head; + elem_t elem0, elem1, elem2; + elem_t *elem; + + ucs_queue_head_init(&head); + EXPECT_TRUE(ucs_queue_is_empty(&head)); + + elem0.i = 0; + elem1.i = 1; + elem2.i = 2; + + for (unsigned i = 0; i < 5; ++i) { + ucs_queue_push(&head, &elem0.queue); + EXPECT_FALSE(ucs_queue_is_empty(&head)); + EXPECT_EQ(1, ucs_queue_length(&head)); + + ucs_queue_push(&head, &elem1.queue); + EXPECT_EQ(2, ucs_queue_length(&head)); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem0, elem); + EXPECT_EQ(1, ucs_queue_length(&head)); + + ucs_queue_push(&head, &elem2.queue); + EXPECT_EQ(2, ucs_queue_length(&head)); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem1, elem); + EXPECT_EQ(1, ucs_queue_length(&head)); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem2, elem); + EXPECT_TRUE(ucs_queue_is_empty(&head)); + EXPECT_TRUE(NULL == ucs_queue_pull(&head)); + + /* Push to head now */ + + ucs_queue_push_head(&head, &elem2.queue); + EXPECT_EQ(1, ucs_queue_length(&head)); + + ucs_queue_push_head(&head, &elem1.queue); + ucs_queue_push_head(&head, &elem0.queue); + EXPECT_EQ(3, ucs_queue_length(&head)); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem0, elem); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem1, elem); + + elem = ucs_queue_pull_elem_non_empty(&head, elem_t, queue); + EXPECT_EQ(&elem2, elem); + + EXPECT_TRUE(ucs_queue_is_empty(&head)); + } +} + +UCS_TEST_F(test_datatype, queue_iter) { + + ucs_queue_head_t head; + elem_t *elem1, *elem2, *elem3, *elem4; + + ucs_queue_head_init(&head); + EXPECT_TRUE(ucs_queue_is_empty(&head)); + + elem1 = (elem_t*)malloc(sizeof(elem_t)); + elem2 = (elem_t*)malloc(sizeof(elem_t)); + elem3 = (elem_t*)malloc(sizeof(elem_t)); + elem4 = (elem_t*)malloc(sizeof(elem_t)); + + elem1->i = 1; + elem2->i = 2; + elem3->i = 3; + elem4->i = 4; + + ucs_queue_push(&head, &elem1->queue); + ucs_queue_push(&head, &elem2->queue); + ucs_queue_push(&head, &elem3->queue); + ucs_queue_push(&head, &elem4->queue); + + + { + std::vector vec; + elem_t *elem; + + ucs_queue_for_each(elem, &head, queue) { + vec.push_back(elem->i); + } + ASSERT_EQ(4u, vec.size()); + EXPECT_EQ(1, vec[0]); + EXPECT_EQ(2, vec[1]); + EXPECT_EQ(3, vec[2]); + EXPECT_EQ(4, vec[3]); + } + + { + std::vector vec; + ucs_queue_iter_t iter; + elem_t *elem; + + ucs_queue_for_each_safe(elem, iter, &head, queue) + { + if (elem->i == 3 || elem->i == 4) { + ucs_queue_del_iter(&head, iter); + free(elem); + } + } + ASSERT_EQ(2, ucs_queue_length(&head)); + + ucs_queue_for_each_safe(elem, iter, &head, queue) { + vec.push_back(elem->i); + ucs_queue_del_iter(&head, iter); + free(elem); + } + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ(1, vec[0]); + EXPECT_EQ(2, vec[1]); + } +} + +UCS_TEST_F(test_datatype, queue_perf) { + const size_t count = 100000000ul; + ucs_queue_head_t head; + ucs_queue_elem_t elem; + + if (ucs::test_time_multiplier() > 1) { + UCS_TEST_SKIP; + } + + ucs_queue_head_init(&head); + ucs_queue_push(&head, &elem); + elem.next = NULL; + + ucs_time_t start_time = ucs_get_time(); + for (size_t i = 0; i < count; ++i) { + ucs_queue_pull(&head); + ucs_queue_push(&head, &elem); + } + ucs_time_t end_time = ucs_get_time(); + + double lat = ucs_time_to_nsec(end_time - start_time) / count; + UCS_TEST_MESSAGE << lat << " nsec per push+pull"; + + EXPECT_LT(lat, 10.0 * ucs::test_time_multiplier()); + EXPECT_EQ(1, ucs_queue_length(&head)); +} + +UCS_TEST_F(test_datatype, queue_splice) { + ucs_queue_head_t head; + elem_t elem0, elem1, elem2; + elem_t *elem; + + elem0.i = 0; + elem1.i = 1; + elem2.i = 2; + + ucs_queue_head_init(&head); + ucs_queue_push(&head, &elem0.queue); + ucs_queue_push(&head, &elem1.queue); + ucs_queue_push(&head, &elem2.queue); + + ucs_queue_head_t newq; + ucs_queue_head_init(&newq); + + EXPECT_EQ(3, ucs_queue_length(&head)); + EXPECT_EQ(0, ucs_queue_length(&newq)); + + ucs_queue_splice(&newq, &head); + + EXPECT_EQ(0, ucs_queue_length(&head)); + EXPECT_EQ(3, ucs_queue_length(&newq)); + + elem = ucs_queue_pull_elem_non_empty(&newq, elem_t, queue); + EXPECT_EQ(&elem0, elem); + + elem = ucs_queue_pull_elem_non_empty(&newq, elem_t, queue); + EXPECT_EQ(&elem1, elem); + + elem = ucs_queue_pull_elem_non_empty(&newq, elem_t, queue); + EXPECT_EQ(&elem2, elem); +} + +UCS_TEST_F(test_datatype, ptr_array_basic) { + ucs_ptr_array_t pa; + uint32_t value; + int a = 1, b = 2, c = 3, d = 4; + unsigned index; + + ucs_ptr_array_init(&pa, 3); + + index = ucs_ptr_array_insert(&pa, &a, &value UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(0u, index); + EXPECT_EQ(3u, value); + + index = ucs_ptr_array_insert(&pa, &b, &value UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(1u, index); + EXPECT_EQ(3u, value); + + index = ucs_ptr_array_insert(&pa, &c, &value UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(2u, index); + EXPECT_EQ(3u, value); + + index = ucs_ptr_array_insert(&pa, &d, &value UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(3u, index); + EXPECT_EQ(3u, value); + + void *vc; + int present = ucs_ptr_array_lookup(&pa, 2, vc); + ASSERT_TRUE(present); + EXPECT_EQ(&c, vc); + + EXPECT_FALSE(ucs_ptr_array_lookup(&pa, 5, vc)); + EXPECT_FALSE(ucs_ptr_array_lookup(&pa, 5005, vc)); + + ucs_ptr_array_remove(&pa, 0, 0); + ucs_ptr_array_remove(&pa, 1, 0); + ucs_ptr_array_remove(&pa, 2, 0); + ucs_ptr_array_remove(&pa, 3, 0); + + ucs_ptr_array_cleanup(&pa); +} + +UCS_TEST_F(test_datatype, ptr_array_random) { + const unsigned count = 10000 / ucs::test_time_multiplier(); + ucs_ptr_array_t pa; + uint32_t value; + + ucs_ptr_array_init(&pa, 5); + + std::map map; + + /* Insert phase */ + for (unsigned i = 0; i < count; ++i) { + void *ptr = malloc(0); + unsigned index = ucs_ptr_array_insert(&pa, ptr, &value UCS_MEMTRACK_NAME("ptr_array test")); + + EXPECT_TRUE(map.end() == map.find(index)); + EXPECT_EQ(5u, value); + map[index] = ptr; + } + + /* Remove + insert */ + for (unsigned i = 0; i < count / 10; ++i) { + + int remove_count = rand() % 10; + for (int j = 0; j < remove_count; ++j) { + unsigned to_remove = rand() % map.size(); + unsigned index = boost::next(map.begin(), to_remove)->first; + + void *ptr; + EXPECT_TRUE(ucs_ptr_array_lookup(&pa, index, ptr)); + EXPECT_EQ(ptr, map[index]); + free(ptr); + + ucs_ptr_array_remove(&pa, index, index * index); + EXPECT_FALSE(ucs_ptr_array_lookup(&pa, index, ptr)); + + map.erase(index); + } + + int insert_count = rand() % 10; + for (int j = 0; j < insert_count; ++j) { + void *ptr = malloc(0); + unsigned index = ucs_ptr_array_insert(&pa, ptr, &value + UCS_MEMTRACK_NAME("ptr_array test")); + + EXPECT_TRUE(map.end() == map.find(index)); + EXPECT_TRUE(index * index == value || 5u == value); + map[index] = ptr; + } + } + + /* remove all */ + void *ptr; + unsigned index; + ucs_ptr_array_for_each(ptr, index, &pa) { + EXPECT_EQ(ptr, map[index]); + ucs_ptr_array_remove(&pa, index, 0); + free(ptr); + } + + ucs_ptr_array_cleanup(&pa); +} + +UCS_TEST_F(test_datatype, ptr_array_placeholder) { + ucs_ptr_array_t pa; + uint32_t value; + int a = 1; + unsigned index; + + ucs_ptr_array_init(&pa, 3); + + index = ucs_ptr_array_insert(&pa, &a, &value + UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(0u, index); + EXPECT_EQ(3u, value); + + ucs_ptr_array_remove(&pa, index, 4); + + index = ucs_ptr_array_insert(&pa, &a, &value + UCS_MEMTRACK_NAME("ptr_array test")); + EXPECT_EQ(0u, index); + EXPECT_EQ(4u, value); + + ucs_ptr_array_remove(&pa, index, 0); + + ucs_ptr_array_cleanup(&pa); +} + +UCS_TEST_F(test_datatype, ptr_array_perf) { + const unsigned count = 10000000; + ucs_ptr_array_t pa; + uint32_t value; + + if (ucs::test_time_multiplier() > 1) { + UCS_TEST_SKIP; + } + + ucs_time_t insert_start_time = ucs_get_time(); + ucs_ptr_array_init(&pa, 0); + for (unsigned i = 0; i < count; ++i) { + EXPECT_EQ(i, ucs_ptr_array_insert(&pa, NULL, &value + UCS_MEMTRACK_NAME("ptr_array test"))); + } + + ucs_time_t lookup_start_time = ucs_get_time(); + for (unsigned i = 0; i < count; ++i) { + void *ptr; + int present = ucs_ptr_array_lookup(&pa, i, ptr); + ASSERT_TRUE(present); + } + + ucs_time_t remove_start_time = ucs_get_time(); + for (unsigned i = 0; i < count; ++i) { + ucs_ptr_array_remove(&pa, i, 0); + } + + ucs_time_t end_time = ucs_get_time(); + + ucs_ptr_array_cleanup(&pa); + + double insert_ns = ucs_time_to_nsec(lookup_start_time - insert_start_time) / count; + double lookup_ns = ucs_time_to_nsec(remove_start_time - lookup_start_time) / count; + double remove_ns = ucs_time_to_nsec(end_time - remove_start_time) / count; + + UCS_TEST_MESSAGE << "Timings (nsec): insert " << insert_ns << " lookup: " << + lookup_ns << " remove: " << remove_ns; + + EXPECT_LT(insert_ns, 1000.0); + EXPECT_LT(remove_ns, 1000.0); + EXPECT_LT(lookup_ns, 10.0); +} + +typedef struct { + int me; + int ncalls; + bool remove; + ucs_notifier_chain_t *chain; +} notifier_test_cb_t; + +static void notifier_test_func(notifier_test_cb_t *cb) { + cb->ncalls++; + if (cb->remove) { + ucs_notifier_chain_remove(cb->chain, (ucs_notifier_chain_func_t)notifier_test_func, cb); + } +} + +UCS_TEST_F(test_datatype, notifier_chain) { + + static size_t N_CALLS = 1000; + + ucs_notifier_chain_t chain; + std::vector elems(3); + + ucs_notifier_chain_init(&chain); + + int i = 0; + BOOST_FOREACH(notifier_test_cb_t& cb, elems) { + cb.me = i++; + cb.ncalls = 0; + cb.remove = 0; + cb.chain = &chain; + ucs_notifier_chain_add(&chain, (ucs_notifier_chain_func_t)notifier_test_func, &cb); + } + + { + for (size_t i = 0; i < N_CALLS; ++i) { + ucs_notifier_chain_call(&chain); + } + EXPECT_NEAR(N_CALLS, elems[0].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS, elems[1].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS, elems[2].ncalls, N_CALLS/100); + } + + elems[1].remove = true; + + { + for (size_t i = 0; i < N_CALLS; ++i) { + ucs_notifier_chain_call(&chain); + } + EXPECT_NEAR(N_CALLS*2, elems[0].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS, elems[1].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS*2, elems[2].ncalls, N_CALLS/100); + } + + elems[0].remove = true; + + { + for (size_t i = 0; i < N_CALLS; ++i) { + ucs_notifier_chain_call(&chain); + } + EXPECT_NEAR(N_CALLS*2, elems[0].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS , elems[1].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS*3, elems[2].ncalls, N_CALLS/100); + } + + elems[2].remove = true; + + { + for (size_t i = 0; i < N_CALLS; ++i) { + ucs_notifier_chain_call(&chain); + } + EXPECT_NEAR(N_CALLS*2, elems[0].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS , elems[1].ncalls, N_CALLS/100); + EXPECT_NEAR(N_CALLS*3, elems[2].ncalls, N_CALLS/100); + } + + +} diff --git a/test/gtest/ucs/test_debug.cc b/test/gtest/ucs/test_debug.cc new file mode 100644 index 00000000000..e79cb39a32b --- /dev/null +++ b/test/gtest/ucs/test_debug.cc @@ -0,0 +1,116 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +#include +#include +} + +#include +#include + +extern "C" { + +int UCS_F_NOINLINE my_cool_function(unsigned *lineno, void **address) +{ + int a; + + a = 5; + + ucs_compiler_fence(); +label1: *lineno = __LINE__; + ucs_compiler_fence(); + *address = &&label1; + + ++a; + return a; +} + +} + +class test_debug : public ucs::test { +}; + +std::string __basename(const std::string& path) { + char *p = strdup(path.c_str()); + std::string bn(::basename(p)); + free(p); + return bn; +} + +UCS_TEST_F(test_debug, lookup_func) { + ucs_debug_address_info info; + ucs_status_t status = ucs_debug_lookup_address((void*)my_cool_function, &info); + ASSERT_UCS_OK(status); + + EXPECT_NE(std::string::npos, std::string(info.file.path).find("gtest")); +#ifdef HAVE_DETAILED_BACKTRACE + EXPECT_EQ("my_cool_function", std::string(info.function)) << (void*)my_cool_function; +#endif +} + +UCS_TEST_F(test_debug, lookup_ucs_func) { + const char sym[] = "ucs_log_flush"; + + ucs_debug_address_info info; + ucs_status_t status = ucs_debug_lookup_address(dlsym(RTLD_DEFAULT, sym), &info); + ASSERT_UCS_OK(status); + + EXPECT_NE(std::string::npos, std::string(info.file.path).find("libucs.so")); +#ifdef HAVE_DETAILED_BACKTRACE + EXPECT_EQ(sym, std::string(info.function)); +#endif +} + +UCS_TEST_F(test_debug, lookup_invalid) { + ucs_debug_address_info info; + ucs_status_t status = ucs_debug_lookup_address((void*)0xffffffffffff, &info); + EXPECT_EQ(UCS_ERR_NO_ELEM, status); +} + +UCS_TEST_F(test_debug, lookup_address) { + unsigned lineno; + void *address; + + if (BULLSEYE_ON) { + UCS_TEST_SKIP; + } + + my_cool_function(&lineno, &address); + + ucs_debug_address_info info; + ucs_status_t status = ucs_debug_lookup_address(address, &info); + ASSERT_UCS_OK(status); + + EXPECT_NE(std::string::npos, std::string(info.file.path).find("gtest")); + +#ifdef HAVE_DETAILED_BACKTRACE + EXPECT_EQ("my_cool_function", std::string(info.function)); + EXPECT_EQ(lineno, info.line_number); + EXPECT_EQ(__basename(__FILE__), __basename(info.source_file)); +#else + EXPECT_EQ(0, info.line_number); + EXPECT_EQ("", std::string(info.source_file)); +#endif +} + +UCS_TEST_F(test_debug, print_backtrace) { + char *data; + size_t size; + + FILE *f = open_memstream(&data, &size); + ucs_debug_print_backtrace(f, 0); + fclose(f); + + /* Some functions that should appear */ + EXPECT_TRUE(strstr(data, "print_backtrace") != NULL); + EXPECT_TRUE(strstr(data, "main") != NULL); + + free(data); +} diff --git a/test/gtest/ucs/test_frag_list.cc b/test/gtest/ucs/test_frag_list.cc new file mode 100644 index 00000000000..903c7085627 --- /dev/null +++ b/test/gtest/ucs/test_frag_list.cc @@ -0,0 +1,336 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include + +class frag_list : public ucs::test { +protected: + struct pkt { + uint32_t sn; + ucs_frag_list_elem_t elem; + }; + ucs_frag_list_t m_frags; + // @override + virtual void init(); + virtual void cleanup(); + + void init_pkts(pkt *packets, int n); + void permute_array(int *arr, int n); + +}; + +void frag_list::permute_array(int *arr, int n) +{ + + int i; + int idx; + int tmp; + + for (i = 0; i < n; i++) { + arr[i] = i; + } + + for (i = 0; i < n - 1; i++) { + idx = i + ::rand() % (n - i); + tmp = arr[i]; + arr[i] = arr[idx]; + arr[idx] = tmp; + } +} + +void frag_list::init_pkts(pkt *packets, int n) +{ + int i; + + for (i = 0; i < n; i++) { + packets[i].sn = i; + } +} + +void frag_list::init() +{ + ::srand(::time(NULL)); + ucs_stats_cleanup(); +#if ENABLE_STATS + push_config(); + set_config("STATS_DEST", "stdout"); + set_config("STATS_TRIGGER", ""); +#endif + ucs_stats_init(); + ucs_frag_list_init(0, &m_frags, -1 UCS_STATS_ARG(NULL)); +} + +void frag_list::cleanup() +{ + ucs_frag_list_cleanup(&m_frags); + ucs_stats_cleanup(); +#if ENABLE_STATS + pop_config(); +#endif + ucs_stats_init(); +} + + +/* next four tests cover all possible insertions and removals. */ + +/** + * rcv in order + */ +UCS_TEST_F(frag_list, in_order_rcv) { + ucs_frag_list_elem_t pkt; + unsigned i; + int err; + + err = ucs_frag_list_insert(&m_frags, &pkt, 0); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_DUP, err); + err = ucs_frag_list_insert(&m_frags, &pkt, (unsigned)(-1)); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_DUP, err); + + for (i = 1; i < 10; i++) { + err = ucs_frag_list_insert(&m_frags, &pkt, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FAST, err); + } +#if ENABLE_STATS + EXPECT_EQ(1, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURSTS)); + EXPECT_EQ(9, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURST_LEN)); + EXPECT_EQ(0, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAPS)); + EXPECT_EQ(0, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_LEN)); + EXPECT_EQ(0, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_OUT)); +#endif +} + +/** + * one hole in front + */ +UCS_TEST_F(frag_list, one_hole) { + pkt pkts[10], *out; + ucs_frag_list_elem_t *elem; + unsigned i; + int err; + + init_pkts(pkts, 10); + + for (i = 5; i < 10; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + /* try to pull - should fail */ + elem = ucs_frag_list_pull(&m_frags); + EXPECT_EQ((void *)elem, (void *)NULL); + + /* insert 1-3: no need to pull more elems from list + * insert 4: more elems can be pulled + */ + for (i = 1; i < 5; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + if (i < 4) { + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FAST, err); + } + else { + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FIRST, err); + } + } + + /* sn 5 already in - next one must fail */ + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, 5); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_DUP, err); + + + i = 0; + /* elem 5..9 must be on the list now */ + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + EXPECT_EQ(out->sn, i+5); + i++; + } + EXPECT_EQ(5, i); +#if ENABLE_STATS + EXPECT_EQ(2, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURSTS)); + EXPECT_EQ(10, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURST_LEN)); + EXPECT_EQ(1, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAPS)); + EXPECT_EQ(5, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_LEN)); + EXPECT_EQ(9, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_OUT)); +#endif +} + +UCS_TEST_F(frag_list, two_holes_basic) { + pkt pkts[20], *out; + ucs_frag_list_elem_t *elem; + unsigned i; + int err; + + init_pkts(pkts, 20); + + + for (i = 15; i < 20; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + /* try to pull - should fail */ + elem = ucs_frag_list_pull(&m_frags); + EXPECT_EQ((void *)NULL, (void *)elem); + + for (i = 5; i < 10; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(err, UCS_FRAG_LIST_INSERT_SLOW); + } + + /* try to pull - should fail */ + elem = ucs_frag_list_pull(&m_frags); + EXPECT_EQ((void *)NULL, (void *)elem); + + for (i = 4; i > 1; i--) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(err, UCS_FRAG_LIST_INSERT_SLOW); + } + + err = ucs_frag_list_insert(&m_frags, &pkts[1].elem, 1); + EXPECT_EQ(err, UCS_FRAG_LIST_INSERT_FIRST); + + i = 2; + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + EXPECT_EQ(out->sn, i); + i++; + } + EXPECT_EQ(i, 10); + + for (i = 10; i < 15; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + if (i < 14) { + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FAST, err); + } + else { + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FIRST, err); + } + } + + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + EXPECT_EQ(out->sn, i); + i++; + } + EXPECT_EQ(20, i); +#if ENABLE_STATS + EXPECT_EQ(7, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURSTS)); + EXPECT_EQ(19, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_BURST_LEN)); + EXPECT_EQ(2, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAPS)); + EXPECT_EQ(20, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_LEN)); + EXPECT_EQ(28, UCS_STATS_GET_COUNTER(m_frags.stats, UCS_FRAG_LIST_STAT_GAP_OUT)); +#endif +} + +/** + * two holes + */ +UCS_TEST_F(frag_list, two_holes_advanced) { + pkt pkts[20], *out; + ucs_frag_list_elem_t *elem; + unsigned i; + int err; + + init_pkts(pkts, 20); + + for (i = 5; i < 10; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + /* try to pull - should fail */ + elem = ucs_frag_list_pull(&m_frags); + EXPECT_EQ((void *)NULL, (void *)elem); + + for (i = 13; i < 18; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + for (i = 19; i >= 18; i--) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + for (i = 12; i >= 10; i--) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + for (i = 4; i > 1; i--) { + err = ucs_frag_list_insert(&m_frags, &pkts[i].elem, i); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_SLOW, err); + } + + err = ucs_frag_list_insert(&m_frags, &pkts[1].elem, 1); + EXPECT_EQ(UCS_FRAG_LIST_INSERT_FIRST, err); + + i = 2; + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + EXPECT_EQ(out->sn, i); + i++; + } + EXPECT_EQ(20, i); +} + +/** + * + * random arrival. Send/recv 10k packets in random order + */ +#define FRAG_LIST_N_PKTS 10000 + +UCS_TEST_F(frag_list, random_arrival) { + pkt pkts[FRAG_LIST_N_PKTS+1], *out; + ucs_frag_list_elem_t *elem; + unsigned i; + int idx[FRAG_LIST_N_PKTS]; + int err; + int fast_inserts, slow_inserts, pulled; + uint32_t last_sn = 0; + uint32_t max_holes=0, max_elems=0; + + init_pkts(pkts, FRAG_LIST_N_PKTS+1); + + permute_array(idx, FRAG_LIST_N_PKTS); + + fast_inserts = slow_inserts = pulled = 0; + for (i = 0; i < FRAG_LIST_N_PKTS; i++) { + err = ucs_frag_list_insert(&m_frags, &pkts[idx[i]+1].elem, idx[i]+1); + EXPECT_NE(err, UCS_FRAG_LIST_INSERT_DUP); + if (err == UCS_FRAG_LIST_INSERT_FAST || err == UCS_FRAG_LIST_INSERT_FIRST) { + fast_inserts++; + EXPECT_EQ(last_sn+1, idx[i]+1); + last_sn = idx[i]+1; + } + else { + slow_inserts++; + } + max_holes = ucs_max(m_frags.list_count, max_holes); + max_elems = ucs_max(m_frags.elem_count, max_elems); + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + pulled++; + EXPECT_EQ(last_sn+1, out->sn); + last_sn = out->sn; + } + } + ucs_frag_list_dump(&m_frags, 0); + UCS_TEST_MESSAGE << "max_holes=" << max_holes << " max_elems=" << max_elems; + UCS_TEST_MESSAGE << "fast_ins=" << fast_inserts <<" slow_ins=" << slow_inserts << " pulled=" << pulled; + while((elem = ucs_frag_list_pull(&m_frags)) != NULL) { + out = ucs_container_of(elem, pkt, elem); + EXPECT_EQ(last_sn+1, out->sn); + last_sn = out->sn; + } +} + diff --git a/test/gtest/ucs/test_instr.cc b/test/gtest/ucs/test_instr.cc new file mode 100644 index 00000000000..4567fb5b55a --- /dev/null +++ b/test/gtest/ucs/test_instr.cc @@ -0,0 +1,119 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +#include +#include +} + +#include + +#if HAVE_INSTRUMENTATION + +class instrument : public ucs::test { +protected: + virtual void init() { + ucs_instrument_cleanup(); + push_config(); + set_config("INSTRUMENT", UCS_INSTR_FILENAME); + } + + virtual void cleanup() { + unlink(UCS_INSTR_FILENAME); + pop_config(); + ucs_instrument_init(); + } + +protected: + static const char* UCS_INSTR_FILENAME; +}; + +const char* instrument::UCS_INSTR_FILENAME = "ucs.instr"; + +#define UCS_TEST_INSTR_LPARAM 0xdeadbeefdeadbeefull +#define UCS_TEST_INSTR_WPARAM 0x1ee7feed + +static void test_func(ucs_debug_address_info_t *exp_info, unsigned long *address) +{ + /* BEGIN the code should not be rearranged or test may fail */ +label_before: + UCS_INSTRUMENT_RECORD(UCS_TEST_INSTR_LPARAM, UCS_TEST_INSTR_WPARAM); exp_info->line_number = __LINE__; + /* END */ + + *address = reinterpret_cast(&&label_before); + strncpy(exp_info->function, __FUNCTION__, sizeof(exp_info->function)); +} + + +UCS_TEST_F(instrument, record) { + ucs_debug_address_info_t exp_info; + unsigned long address; + ucs_instrument_header_t hdr; + + /* Perform instrumentation */ + ucs_instrument_init(); + test_func(&exp_info, &address); + ucs_instrument_cleanup(); + + /* Read the file */ + ucs_instrument_record_t record; + std::ifstream fi(UCS_INSTR_FILENAME); + ASSERT_FALSE(fi.bad()); + + fi.read(reinterpret_cast(&hdr), sizeof(ucs_instrument_header_t)); + fi.read(reinterpret_cast(&record), sizeof(record)); + +// ASSERT_EQ(UCS_API, hdr.ucs_lib.api_version); TODO + if (ucs::test_time_multiplier() == 1) { + EXPECT_LE(record.timestamp, ucs_get_time()); + EXPECT_GE(record.timestamp, ucs_get_time() - ucs_time_from_sec(0.1)); + } + EXPECT_EQ(UCS_TEST_INSTR_LPARAM, record.lparam); + EXPECT_EQ(UCS_TEST_INSTR_WPARAM, record.wparam); + EXPECT_GE(record.location, static_cast(address)); + +#if HAVE_DETAILED_BACKTRACE + ucs_debug_address_info_t info; + ucs_debug_get_line_info(ucs_get_exe(), 0, record.location, &info); + EXPECT_EQ(exp_info.line_number, info.line_number); + EXPECT_EQ(0, strcmp(exp_info.function, info.function)) << + info.function << " != " << exp_info.function; +#endif +} + +UCS_TEST_F(instrument, overhead) { + static const size_t count = 10000000; + ucs_time_t elapsed1, elapsed2; + + if (ucs::test_time_multiplier() > 1) { + UCS_TEST_SKIP; + } + + { + ucs_time_t start_time = ucs_get_time(); + for (size_t i = 0; i < count; ++i) { + } + elapsed1 = ucs_get_time() - start_time; + } + + { + ucs_instrument_init(); + ucs_time_t start_time = ucs_get_time(); + for (size_t i = 0; i < count; ++i) { + UCS_INSTRUMENT_RECORD(0xdeadbeef); + } + elapsed2 = ucs_get_time() - start_time; + ucs_instrument_cleanup(); + } + + double overhead_nsec = ucs_time_to_nsec(elapsed2 - elapsed1) / count; + UCS_TEST_MESSAGE << "Overhead is " << overhead_nsec << " nsec"; +} + +#endif diff --git a/test/gtest/ucs/test_math.cc b/test/gtest/ucs/test_math.cc new file mode 100644 index 00000000000..91497b99b12 --- /dev/null +++ b/test/gtest/ucs/test_math.cc @@ -0,0 +1,163 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include + +#define FLAG1 0x100 +#define FLAG2 0x200 +#define FLAG3 0x400 + +class test_math : public ucs::test { +protected: + static const unsigned ATOMIC_COUNT = 50; +}; + +UCS_TEST_F(test_math, convert_flag) { + volatile uint32_t value = FLAG1 | FLAG3; + volatile uint32_t tmp = ucs_convert_flag(value, FLAG1, 0x1); + + EXPECT_EQ(0x1u, tmp); + EXPECT_EQ(0x0u, ucs_convert_flag(value, FLAG2, 0x2u)); + EXPECT_EQ(0x4u, ucs_convert_flag(value, FLAG3, 0x4u)); + + EXPECT_EQ(0x10000u, ucs_convert_flag(value, FLAG1, 0x10000u)); + EXPECT_EQ(0x00000u, ucs_convert_flag(value, FLAG2, 0x20000u)); + EXPECT_EQ(0x40000u, ucs_convert_flag(value, FLAG3, 0x40000u)); +} + +UCS_TEST_F(test_math, test_flag) { + uint32_t value = FLAG2; + EXPECT_TRUE( ucs_test_flags(value, FLAG1, FLAG2) ); + EXPECT_TRUE( ucs_test_flags(value, FLAG2, FLAG3) ); + EXPECT_FALSE( ucs_test_flags(value, FLAG1, FLAG3) ); +} + +UCS_TEST_F(test_math, circular_compare) { + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0x000000001, <, 0x000000002) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0x000000001, ==, 0x000000001) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0xffffffffU, >, 0xfffffffeU) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0xffffffffU, <, 0x00000000U) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0xffffffffU, <, 0x00000001U) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0xffffffffU, <, 0x00000001U) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0x80000000U, >, 0x7fffffffU) ); + EXPECT_TRUE( UCS_CIRCULAR_COMPARE32(0xffffffffU, <, 0x7fffffffU) ); +} + +UCS_TEST_F(test_math, bitops) { + EXPECT_EQ(0, ucs_ffs64(0xfffff)); + EXPECT_EQ(16, ucs_ffs64(0xf0000)); + EXPECT_EQ(1, ucs_ffs64(0x4002)); + EXPECT_EQ(41, ucs_ffs64(1ull<<41)); + + EXPECT_EQ(0, ucs_ilog2(1)); + EXPECT_EQ(2, ucs_ilog2(4)); + EXPECT_EQ(2, ucs_ilog2(5)); + EXPECT_EQ(2, ucs_ilog2(7)); + EXPECT_EQ(14, ucs_ilog2(17000)); + EXPECT_EQ(40, ucs_ilog2(1ull<<40)); +} + +#define TEST_ATOMIC_ADD(_bitsize) \ + { \ + typedef uint##_bitsize##_t inttype; \ + const inttype var_value = ucs::random_upper(); \ + const inttype add_value = ucs::random_upper(); \ + inttype var = var_value; \ + ucs_atomic_add##_bitsize(&var, add_value); \ + EXPECT_EQ(static_cast(var_value + add_value), var); \ + } + +#define TEST_ATOMIC_FADD(_bitsize) \ + { \ + typedef uint##_bitsize##_t inttype; \ + const inttype var_value = ucs::random_upper(); \ + const inttype add_value = ucs::random_upper(); \ + inttype var = var_value; \ + inttype oldvar = ucs_atomic_fadd##_bitsize(&var, add_value); \ + EXPECT_EQ(static_cast(var_value + add_value), var); \ + EXPECT_EQ(var_value, oldvar); \ + } + +#define TEST_ATOMIC_SWAP(_bitsize) \ + { \ + typedef uint##_bitsize##_t inttype; \ + const inttype var_value = ucs::random_upper(); \ + const inttype swap_value = ucs::random_upper(); \ + inttype var = var_value; \ + inttype oldvar = ucs_atomic_swap##_bitsize(&var, swap_value); \ + EXPECT_EQ(var_value, oldvar); \ + EXPECT_EQ(swap_value, var); \ + } + +#define TEST_ATOMIC_CSWAP(_bitsize, is_eq) \ + { \ + typedef uint##_bitsize##_t inttype; \ + const inttype var_value = ucs::random_upper(); \ + const inttype cmp_value = (is_eq) ? var_value : (var_value + 10); \ + const inttype swap_value = ucs::random_upper(); \ + inttype var = var_value; \ + inttype oldvar = ucs_atomic_cswap##_bitsize(&var, cmp_value, swap_value); \ + EXPECT_EQ(var_value, oldvar); \ + if (is_eq) { \ + EXPECT_EQ(swap_value, var); \ + } else { \ + EXPECT_EQ(var_value, var); \ + } \ + } + +UCS_TEST_F(test_math, atomic_add) { + for (unsigned count = 0; count < ATOMIC_COUNT; ++count) { + TEST_ATOMIC_ADD(8); + TEST_ATOMIC_ADD(16); + TEST_ATOMIC_ADD(32); + TEST_ATOMIC_ADD(64); + } +} + +UCS_TEST_F(test_math, atomic_fadd) { + for (unsigned count = 0; count < ATOMIC_COUNT; ++count) { + TEST_ATOMIC_FADD(8); + TEST_ATOMIC_FADD(16); + TEST_ATOMIC_FADD(32); + TEST_ATOMIC_FADD(64); + } +} + +UCS_TEST_F(test_math, atomic_swap) { + for (unsigned count = 0; count < ATOMIC_COUNT; ++count) { +#ifndef __powerpc__ + // TODO enable when we'll support atomic swap on powerpc + TEST_ATOMIC_SWAP(8); + TEST_ATOMIC_SWAP(16); + TEST_ATOMIC_SWAP(32); + TEST_ATOMIC_SWAP(64); +#endif + } +} + +UCS_TEST_F(test_math, atomic_cswap_success) { + for (unsigned count = 0; count < ATOMIC_COUNT; ++count) { + TEST_ATOMIC_CSWAP(8, 0); + TEST_ATOMIC_CSWAP(16, 0); + TEST_ATOMIC_CSWAP(32, 0); + TEST_ATOMIC_CSWAP(64, 0); + } +} + +UCS_TEST_F(test_math, atomic_cswap_fail) { + for (unsigned count = 0; count < ATOMIC_COUNT; ++count) { + TEST_ATOMIC_CSWAP(8, 1); + TEST_ATOMIC_CSWAP(16, 1); + TEST_ATOMIC_CSWAP(32, 1); + TEST_ATOMIC_CSWAP(64, 1); + } +} diff --git a/test/gtest/ucs/test_memtrack.cc b/test/gtest/ucs/test_memtrack.cc new file mode 100644 index 00000000000..ce75e2fd311 --- /dev/null +++ b/test/gtest/ucs/test_memtrack.cc @@ -0,0 +1,119 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include + +extern "C" { +#include +} + +#include +#include +#include +#include +#include + +#if ENABLE_MEMTRACK + +class test_memtrack : public ucs::test { +protected: + static const size_t ALLOC_SIZE = 100; + + void init() { + ucs_memtrack_cleanup(); + push_config(); + set_config("MEMTRACK_DEST", "/dev/null"); + ucs_memtrack_init(); + } + + void cleanup() { + ucs_memtrack_cleanup(); + pop_config(); + ucs_memtrack_init(); + } +}; + + +UCS_TEST_F(test_memtrack, sanity) { + char test_str[] = "memtrack_test"; + ucs_memtrack_entry_t entry; + void *a, *b; + int i; + + ucs_memtrack_total(&entry); + i = entry.current_count; + + b = ucs_malloc(1, test_str); + ucs_free(b); + + b = ucs_malloc(1, test_str); + a = ucs_malloc(3, test_str); + ucs_free(b); + ucs_memtrack_total(&entry); + if (ucs_memtrack_is_enabled()) { + ASSERT_EQ(i + 1, entry.current_count); + } + + b = ucs_malloc(4, test_str); + ucs_free(b); + ucs_memtrack_total( &entry); + if (ucs_memtrack_is_enabled()) { + ASSERT_EQ(1, entry.current_count); + } + ucs_free(a); + + for (i = 0; i < 101; i++) { + a = ucs_malloc(i, test_str); + ucs_free(a); + } +} + +UCS_TEST_F(test_memtrack, parse_dump) { + char *buf; + size_t size; + + /* Dump */ + { + FILE* tempf = open_memstream(&buf, &size); + ucs_memtrack_dump(tempf); + fclose(tempf); + } + + /* Parse */ + ASSERT_NE((void *)NULL, strstr(buf, "TOTAL")); + + free(buf); +} + +UCS_TEST_F(test_memtrack, alloc_types) { + char test_str[] = "memtrack_test"; + void* ptr; + + ptr = ucs_malloc(ALLOC_SIZE, test_str); + ASSERT_NE((void *)NULL, ptr); + ptr = ucs_realloc(ptr, 2 * ALLOC_SIZE); + ASSERT_NE((void *)NULL, ptr); + ucs_free(ptr); + + ptr = ucs_calloc(1, ALLOC_SIZE, test_str); + ASSERT_NE((void *)NULL, ptr); + ucs_free(ptr); + + ptr = ucs_calloc(ALLOC_SIZE, 1, test_str); + ASSERT_NE((void *)NULL, ptr); + ucs_free(ptr); + + ptr = ucs_memalign(10, ALLOC_SIZE, test_str); + ASSERT_NE((void *)NULL, ptr); + ucs_free(ptr); + + ptr = ucs_memalign(1000, ALLOC_SIZE, test_str); + ASSERT_NE((void *)NULL, ptr); + ucs_free(ptr); +} + +#endif diff --git a/test/gtest/ucs/test_mpool.cc b/test/gtest/ucs/test_mpool.cc new file mode 100644 index 00000000000..85c9967ed27 --- /dev/null +++ b/test/gtest/ucs/test_mpool.cc @@ -0,0 +1,122 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include +#include + +class test_mpool : public ucs::test { +protected: + static void *test_alloc(size_t *size, void *context UCS_MEMTRACK_ARG) { + return (*(void**)context = malloc(*size)); + } + + static void test_free(void *ptr, void *context) { + free(ptr); + *(void**)context = NULL; + } + + static const size_t header_size = 30; + static const size_t data_size = 152; + static const size_t align = 114; +}; + + +UCS_TEST_F(test_mpool, no_allocs) { + ucs_mpool_h mp; + ucs_status_t status; + + status = ucs_mpool_create("test", header_size + data_size, header_size, align, + 6, 18, NULL, + ucs_mpool_chunk_malloc, + ucs_mpool_chunk_free, + NULL, NULL, &mp); + ASSERT_UCS_OK(status); + ucs_mpool_destroy(mp); +} + +UCS_TEST_F(test_mpool, basic) { + ucs_status_t status; + ucs_mpool_h mp; + + status = ucs_mpool_create("test", header_size + data_size, header_size, align, + 6, 18, NULL, + ucs_mpool_chunk_malloc, + ucs_mpool_chunk_free, + NULL, NULL, &mp); + ASSERT_UCS_OK(status); + + for (unsigned loop = 0; loop < 10; ++loop) { + std::vector objs; + for (unsigned i = 0; i < 18; ++i) { + void *ptr = ucs_mpool_get(mp); + ASSERT_TRUE(ptr != NULL); + ASSERT_EQ(0ul, ((uintptr_t)ptr + header_size) % align) << ptr; + memset(ptr, 0xAA, header_size + data_size); + objs.push_back(ptr); + } + + ASSERT_TRUE(NULL == ucs_mpool_get(mp)); + + for (std::vector::iterator iter = objs.begin(); iter != objs.end(); ++iter) { + ucs_mpool_put(*iter); + } + } + + ucs_mpool_destroy(mp); +} + +UCS_TEST_F(test_mpool, custom_alloc) { + ucs_status_t status; + ucs_mpool_h mp; + void *ptr = NULL; + + status = ucs_mpool_create("test", header_size + data_size, header_size, align, 5, 18, + &ptr, test_alloc, test_free, NULL, NULL, &mp); + ASSERT_UCS_OK(status); + + void *obj = ucs_mpool_get(mp); + EXPECT_TRUE(obj != NULL); + EXPECT_TRUE(ptr != NULL); + + ucs_mpool_put(obj); + + ucs_mpool_destroy(mp); + EXPECT_EQ(NULL, ptr); +} + +UCS_TEST_F(test_mpool, infinite) { + const unsigned NUM_ELEMS = 1000000 / ucs::test_time_multiplier(); + ucs_status_t status; + ucs_mpool_h mp; + + status = ucs_mpool_create("test", header_size + data_size, header_size, align, + 10000, UCS_MPOOL_INFINITE, + NULL, + ucs_mpool_chunk_malloc, + ucs_mpool_chunk_free, + NULL, NULL, &mp); + ASSERT_UCS_OK(status); + + std::queue q; + for (unsigned i = 0; i < NUM_ELEMS; ++i) { + void *obj = ucs_mpool_get(mp); + ASSERT_TRUE(obj != NULL); + q.push(obj); + } + + while (!q.empty()) { + ucs_mpool_put(q.front()); + q.pop(); + } + + ucs_mpool_destroy(mp); +} diff --git a/test/gtest/ucs/test_stats.cc b/test/gtest/ucs/test_stats.cc new file mode 100644 index 00000000000..98b2a66bb99 --- /dev/null +++ b/test/gtest/ucs/test_stats.cc @@ -0,0 +1,287 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include +#include +#include + +#if ENABLE_STATS + +class stats_test : public ucs::test { +public: + + template + struct stats_class { + ucs_stats_class_t cls; + const char *counter_names[N]; + }; + + virtual void init() { + ucs::test::init(); + ucs_stats_cleanup(); + push_config(); + set_config("STATS_DEST", stats_dest_config().c_str()); + set_config("STATS_TRIGGER", stats_trigger_config().c_str()); + ucs_stats_init(); + ASSERT_TRUE(ucs_stats_is_active()); + } + + virtual void cleanup() { + ucs_stats_cleanup(); + pop_config(); + ucs_stats_init(); + ucs::test::cleanup(); + } + + virtual std::string stats_dest_config() = 0; + virtual std::string stats_trigger_config() = 0; + + void prepare_nodes() { + static stats_class<0> category_stats_class = { + {"category", 0, {}} + }; + + static stats_class<4> data_stats_class = { + { "data", NUM_COUNTERS, {} }, + { "counter0","counter1","counter2","counter3" } + }; + + ucs_status_t status = UCS_STATS_NODE_ALLOC(&cat_node, &category_stats_class.cls, NULL); + ASSERT_UCS_OK(status); + for (unsigned i = 0; i < NUM_DATA_NODES; ++i) { + status = UCS_STATS_NODE_ALLOC(&data_nodes[i], &data_stats_class.cls, + cat_node, "-%d", i); + ASSERT_UCS_OK(status); + + UCS_STATS_UPDATE_COUNTER(data_nodes[i], 0, 10); + UCS_STATS_UPDATE_COUNTER(data_nodes[i], 1, 20); + UCS_STATS_UPDATE_COUNTER(data_nodes[i], 2, 30); + UCS_STATS_UPDATE_COUNTER(data_nodes[i], 3, 40); + } + + /* make sure our original node is ok */ + check_cat_node(cat_node); + } + + void free_nodes() { + for (unsigned i = 0; i < NUM_DATA_NODES; ++i) { + UCS_STATS_NODE_FREE(data_nodes[i]); + } + UCS_STATS_NODE_FREE(cat_node); + } + + void check_tree(ucs_stats_node_t *root) { + EXPECT_EQ(1ul, ucs_list_length(&root->children[UCS_STATS_ACTIVE_CHILDREN])); + check_cat_node(ucs_list_head(&root->children[UCS_STATS_ACTIVE_CHILDREN], + ucs_stats_node_t, list)); + } + + void check_cat_node(ucs_stats_node_t *cat_node) { + EXPECT_EQ(std::string("category"), std::string(cat_node->cls->name)); + EXPECT_EQ(0, cat_node->cls->num_counters); + + ucs_stats_node_t *data_node; + ucs_list_for_each(data_node, &cat_node->children[UCS_STATS_ACTIVE_CHILDREN], list) { + EXPECT_EQ(std::string("data"), std::string(data_node->cls->name)); + EXPECT_EQ(unsigned(NUM_COUNTERS), data_node->cls->num_counters); + EXPECT_EQ(std::string("counter0"), std::string(data_node->cls->counter_names[0])); + + EXPECT_EQ(10, data_node->counters[0]); + EXPECT_EQ(20, data_node->counters[1]); + EXPECT_EQ(30, data_node->counters[2]); + EXPECT_EQ(40, data_node->counters[3]); + } + } + +protected: + static const unsigned NUM_DATA_NODES = 20; + static const unsigned NUM_COUNTERS = 4; + + ucs_stats_node_t *cat_node; + ucs_stats_node_t *data_nodes[NUM_DATA_NODES]; +}; + +class stats_udp_test : public stats_test { +public: + virtual void init() { + ucs_status_t status = ucs_stats_server_start(0, &m_server); + ASSERT_UCS_OK(status); + stats_test::init(); + } + + virtual void cleanup() { + stats_test::cleanup(); + ucs_stats_server_destroy(m_server); + } + + virtual std::string stats_dest_config() { + int port = ucs_stats_server_get_port(m_server); + EXPECT_GT(port, 0); + return "udp:localhost:" + boost::lexical_cast(port); + } + + virtual std::string stats_trigger_config() { + return "timer:0.1s"; + } + + void read_and_check_stats() { + ucs_list_link_t *list = ucs_stats_server_get_stats(m_server); + ASSERT_EQ(1ul, ucs_list_length(list)); + check_tree(ucs_list_head(list, ucs_stats_node_t, list)); + ucs_stats_server_purge_stats(m_server); + } + +protected: + ucs_stats_server_h m_server; +}; + +class stats_file_test : public stats_test { +public: + stats_file_test() { + m_pipefds[0] = -1; + m_pipefds[1] = -1; + } + + virtual void init() { + /* Note: this test assumes data <64k, o/w stats dump will block forever */ + int ret = pipe(m_pipefds); + ASSERT_EQ(0, ret); + stats_test::init(); + } + + void close_pipes() + { + close(m_pipefds[0]); + close(m_pipefds[1]); + m_pipefds[0] = -1; + m_pipefds[1] = -1; + } + + virtual void cleanup() { + stats_test::cleanup(); + close_pipes(); + } + + virtual std::string stats_dest_config() { + return "file:/dev/fd/" + boost::lexical_cast(m_pipefds[1]) + ":bin"; + } + + std::string get_data() { + std::string data(65536, '\0'); + ssize_t ret = read(m_pipefds[0], &data[0], data.size()); + EXPECT_GE(ret, 0); + data.resize(ret); + return data; + } + + virtual std::string stats_trigger_config() { + return ""; + } + +protected: + int m_pipefds[2]; +}; + +class stats_on_demand_test : public stats_udp_test { +public: + virtual std::string stats_trigger_config() { + return ""; + } +}; + +class stats_on_signal_test : public stats_udp_test { +public: + virtual std::string stats_trigger_config() { + return "signal:USR1"; + } +}; + +class stats_on_exit_test : public stats_file_test { +public: + virtual std::string stats_dest_config() { + return "file:/dev/fd/" + boost::lexical_cast(m_pipefds[1]); + } + + /* + * we check the dump-on-exit in cleanup method . + */ + virtual void cleanup() { + stats_test::cleanup(); + std::string data = get_data(); + size_t pos = 0; + for (unsigned i = 0; i < NUM_DATA_NODES; ++i) { + std::string node_name = " data-" + boost::lexical_cast(i) + ":"; + pos = data.find(node_name, pos); + EXPECT_NE(pos, std::string::npos) << node_name << " not found"; + for (unsigned j = 0; j < NUM_COUNTERS; ++j) { + std::string value = "counter" + + boost::lexical_cast(j) + + ": " + + boost::lexical_cast((j + 1) * 10); + pos = data.find(value, pos); + EXPECT_NE(pos, std::string::npos) << value << " not found"; + } + } + close_pipes(); + } + + virtual std::string stats_trigger_config() { + return "exit"; + } +}; + + +UCS_TEST_F(stats_udp_test, report) { + prepare_nodes(); + usleep(500 * 1000); + read_and_check_stats(); + free_nodes(); +} + +UCS_TEST_F(stats_file_test, report) { + prepare_nodes(); + ucs_stats_dump(); + free_nodes(); + + std::string data = get_data(); + FILE *f = fmemopen(&data[0], data.size(), "rb"); + ucs_stats_node_t *root; + ucs_status_t status = ucs_stats_deserialize(f, &root); + ASSERT_UCS_OK(status); + fclose(f); + + check_tree(root); + ucs_stats_free(root); +} + +UCS_TEST_F(stats_on_demand_test, report) { + prepare_nodes(); + ucs_stats_dump(); + usleep(10000 * ucs::test_time_multiplier()); + read_and_check_stats(); + free_nodes(); +} + +UCS_TEST_F(stats_on_signal_test, report) { + prepare_nodes(); + kill(getpid(), SIGUSR1); + usleep(10000 * ucs::test_time_multiplier()); + read_and_check_stats(); + free_nodes(); +} + +UCS_TEST_F(stats_on_exit_test, dump) { + prepare_nodes(); + free_nodes(); +} + +#endif diff --git a/test/gtest/ucs/test_sys.cc b/test/gtest/ucs/test_sys.cc new file mode 100644 index 00000000000..920fa46a4d7 --- /dev/null +++ b/test/gtest/ucs/test_sys.cc @@ -0,0 +1,94 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +#include +#include +} + +#include +#include + +class test_sys : public ucs::test { +}; + +UCS_TEST_F(test_sys, uuid) { + std::set uuids; + for (unsigned i = 0; i < 10000; ++i) { + uint64_t uuid = ucs_generate_uuid(0); + std::pair::iterator, bool> ret = uuids.insert(uuid); + ASSERT_TRUE(ret.second); + } +} + +UCS_TEST_F(test_sys, machine_guid) { + uint64_t guid1 = ucs_machine_guid(); + uint64_t guid2 = ucs_machine_guid(); + EXPECT_EQ(guid1, guid2); +} + +UCS_TEST_F(test_sys, spinlock) { + ucs_spinlock_t lock; + pthread_t self; + + self = pthread_self(); + + ucs_spinlock_init(&lock); + + ucs_spin_lock(&lock); + EXPECT_TRUE(ucs_spin_is_owner(&lock, self)); + + ucs_spin_lock(&lock); + EXPECT_TRUE(ucs_spin_is_owner(&lock, self)); + + ucs_spin_unlock(&lock); + EXPECT_TRUE(ucs_spin_is_owner(&lock, self)); + + ucs_spin_unlock(&lock); + EXPECT_FALSE(ucs_spin_is_owner(&lock, self)); +} + +UCS_TEST_F(test_sys, get_mem_prot) { + int x; + + ASSERT_TRUE( ucs_get_mem_prot(&x, sizeof(x)) & PROT_READ ); + ASSERT_TRUE( ucs_get_mem_prot(&x, sizeof(x)) & PROT_WRITE ); + ASSERT_TRUE( ucs_get_mem_prot((void*)&ucs_get_mem_prot, 1) & PROT_EXEC ); + + ucs_time_t start_time = ucs_get_time(); + ucs_get_mem_prot(&x, sizeof(x)); + ucs_time_t duration = ucs_get_time() - start_time; + UCS_TEST_MESSAGE << "Time: " << ucs_time_to_usec(duration) << " us"; +} + +UCS_TEST_F(test_sys, fcntl) { + ucs_status_t status; + int fd, fl; + + fd = open("/dev/null", O_RDONLY); + ASSERT_NE(fd, -1); + + /* Add */ + status = ucs_sys_fcntl_modfl(fd, O_NONBLOCK, 0); + ASSERT_UCS_OK(status); + + fl = fcntl(fd, F_GETFL); + ASSERT_GE(fl, 0); + EXPECT_TRUE(fl & O_NONBLOCK); + + /* Remove */ + status = ucs_sys_fcntl_modfl(fd, 0, O_NONBLOCK); + ASSERT_UCS_OK(status); + + fl = fcntl(fd, F_GETFL); + ASSERT_GE(fl, 0); + EXPECT_FALSE(fl & O_NONBLOCK); + + close(fd); +} diff --git a/test/gtest/ucs/test_timer.cc b/test/gtest/ucs/test_timer.cc new file mode 100644 index 00000000000..4c212a96138 --- /dev/null +++ b/test/gtest/ucs/test_timer.cc @@ -0,0 +1,147 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include + +class test_timer : public ucs::test { +protected: + struct timer_with_counter { + ucs_callback_t cb; + unsigned counter; + }; + + static void timer_func(ucs_callback_t *self) + { + struct timer_with_counter *ctx = + ucs_container_of(self, struct timer_with_counter, cb); + ++ctx->counter; + } +}; + +UCS_TEST_F(test_timer, time_calc) { + double value = rand() % UCS_USEC_PER_SEC; + + EXPECT_NEAR(value * 1000, ucs_time_to_msec(ucs_time_from_sec (value)), 0.000001); + EXPECT_NEAR(value * 1000, ucs_time_to_usec(ucs_time_from_msec(value)), 0.001); + EXPECT_NEAR(value * 1000, ucs_time_to_nsec(ucs_time_from_usec(value)), 1.0); +} + +UCS_TEST_F(test_timer, get_time) { + if (ucs::test_time_multiplier() > 1) { + UCS_TEST_SKIP; + } + + ucs_time_t time1 = ucs_get_time(); + ucs_time_t time2 = ucs_get_time(); + EXPECT_GE(time2, time1); + + ucs_time_t start_time = ucs_get_time(); + ucs_time_t end_time = start_time + ucs_time_from_sec(1); + ucs_time_t current_time; + + time_t system_start_time = time(NULL); + + uint64_t count = 0; + do { + current_time = ucs_get_time(); + ++count; + } while (current_time <= end_time); + + /* Check the sleep interval is correct */ + ASSERT_NEAR(1.0, time(NULL) - system_start_time, 0.000001); + + double nsec = (ucs_time_to_nsec(current_time - start_time)) / count; + EXPECT_LT(nsec, 20.0) << "ucs_get_time() performance is too bad"; +} + +UCS_TEST_F(test_timer, time_shift) { + + double accuracy_usec = ucs_time_to_usec(1ull << ucs_get_short_time_shift()); + + UCS_TEST_MESSAGE << "Short time shift is " << ucs_get_short_time_shift() << " bit," << + " accuracy is " << accuracy_usec << " usec"; + EXPECT_LT(accuracy_usec, 1000.0); +} + +UCS_TEST_F(test_timer, timerq_basic) { + ucs_timer_queue_t timerq; + ucs_status_t status; + + ::srand(::time(NULL)); + for (unsigned test_count = 0; test_count < 500; ++test_count) { + + const ucs_time_t interval1 = (::rand() % 20) + 1; + const ucs_time_t interval2 = (::rand() % 20) + 1; + const ucs_time_t test_time = ::rand() % 10000; + const ucs_time_t time_base = ::rand(); + + struct timer_with_counter timerctx1; + struct timer_with_counter timerctx2; + + status = ucs_timerq_init(&timerq); + ASSERT_UCS_OK(status); + + timerctx1.cb.func = timer_func; + timerctx2.cb.func = timer_func; + + ucs_time_t current_time = time_base; + + ucs_timer_add(&timerq, &timerctx1.cb, interval1); + ucs_timer_add(&timerq, &timerctx2.cb, interval2); + + /* + * Check that both timers are invoked + */ + timerctx1.counter = 0; + timerctx2.counter = 0; + for (unsigned count = 0; count < test_time; ++count) { + ++current_time; + ucs_timerq_sweep(&timerq, current_time); + } + EXPECT_NEAR(test_time / interval1, timerctx1.counter, 1); + EXPECT_NEAR(test_time / interval2, timerctx2.counter, 1); + + /* + * Check that after canceling, only one timer is invoked + */ + timerctx1.counter = 0; + timerctx2.counter = 0; + ucs_timer_remove(&timerq, &timerctx1.cb); + for (unsigned count = 0; count < test_time; ++count) { + ++current_time; + ucs_timerq_sweep(&timerq, current_time); + } + EXPECT_EQ(0u, timerctx1.counter); + EXPECT_NEAR(test_time / interval2, timerctx2.counter, 1); + + /* + * Check that after rescheduling, both timers are invoked again + */ + ucs_timer_add(&timerq, &timerctx1.cb, interval1); + + timerctx1.counter = 0; + timerctx2.counter = 0; + for (unsigned count = 0; count < test_time; ++count) { + ++current_time; + ucs_timerq_sweep(&timerq, current_time); + } + EXPECT_NEAR(test_time / interval1, timerctx1.counter, 1); + EXPECT_NEAR(test_time / interval2, timerctx2.counter, 1); + + ucs_timer_remove(&timerq, &timerctx1.cb); + ucs_timer_remove(&timerq, &timerctx2.cb); + + ucs_timerq_cleanup(&timerq); + } +} + + diff --git a/test/gtest/ucs/test_twheel.cc b/test/gtest/ucs/test_twheel.cc new file mode 100644 index 00000000000..5bf654bdfc1 --- /dev/null +++ b/test/gtest/ucs/test_twheel.cc @@ -0,0 +1,241 @@ +/** +* Copyright (C) Mellanox Technologies Ltd. 2001-2011. ALL RIGHTS RESERVED. +* +* $COPYRIGHT$ +* $HEADER$ +*/ + +#include +extern "C" { +#include +} + +#include + +/** + * note: the fast timer precision is dependent on context switch latency!!! + * expected timer precision 2x wheel resolution plus some time for processing and + * context switching + */ +class twheel : public ucs::test { +protected: + + struct hr_timer { + ucs_wtimer_t timer; + int tid; + ucs_time_t start_time; + ucs_time_t end_time; + ucs_time_t d; + ucs_time_t total_time; + twheel *self; + }; + + ucs_twheel_t m_wheel; + + // @override + virtual void init(); + + // @override + virtual void cleanup(); + + static void timer_func(ucs_callback_t *self); + void timer_expired(struct hr_timer *t); + void add_timer(struct hr_timer *t); + void init_timer(struct hr_timer *t, int id); + void init_timerv(struct hr_timer *v, int n); + void set_timer_delta(struct hr_timer *t, int how); +}; + +void twheel::init() +{ + ucs_twheel_init(&m_wheel, ucs_time_from_usec(32) * ucs::test_time_multiplier()); + ::srand(::time(NULL)); +} + +void twheel::cleanup() +{ + ucs_twheel_cleanup(&m_wheel); +} + +void twheel::timer_func(ucs_callback_t *self) +{ + struct hr_timer *t = ucs_container_of(self, struct hr_timer, timer); + t->self->timer_expired(t); +} + +void twheel::timer_expired(struct hr_timer *t) +{ + t->total_time += (m_wheel.now - t->start_time); + t->end_time = m_wheel.now; +} + +void twheel::add_timer(struct hr_timer *t) +{ + t->end_time = 0; + ASSERT_EQ(ucs_wtimer_add(&m_wheel, &t->timer, t->d), UCS_OK); + t->start_time = ucs_get_time(); +} + +void twheel::init_timer(struct hr_timer *t, int id) +{ + t->tid = id; + t->total_time = 0; + t->self = this; + ucs_wtimer_init(&t->timer, timer_func); +} + +void twheel::init_timerv(struct hr_timer *v, int n) +{ + for (int i = 0; i < n; i++) { + init_timer(&v[i], i); + } +} + +void twheel::set_timer_delta(struct hr_timer *t, int how) +{ + int slot; + + switch (how) { + case 0: + /* first */ + slot = 1; + break; + case 1: + /* last */ + slot = m_wheel.num_slots - 1; + break; + case 2: + /* middle */ + slot = m_wheel.num_slots / 2; + break; + case -2: + /* overflow */ + slot = m_wheel.num_slots + (::rand() % 1000000); + break; + default: + slot = 1 + ::rand() % (m_wheel.num_slots - 2); + break; + } + + if (how == -2) { + t->d = m_wheel.res + m_wheel.res * (m_wheel.num_slots - 1) / 2; + } else { + t->d = m_wheel.res + m_wheel.res * slot / 2; + } +} + +#define N_LOOPS 20 + +UCS_TEST_F(twheel, precision_single) { + struct hr_timer t; + ucs_time_t now; + int i, k; + int fail_count; + + UCS_TEST_SKIP; // Test is broken + + init_timer(&t, 0); + for (k = 0; k < 10; k++ ) { + set_timer_delta(&t, k); + fail_count = 0; + for (i = 0; i < N_LOOPS; i++) { + t.total_time = 0; + add_timer(&t); + do { + now = ucs_get_time(); + ucs_twheel_sweep(&m_wheel, now); + } while (t.end_time == 0); + + if (::abs(t.total_time - t.d) > 2 * m_wheel.res) { + ++fail_count; + } + } + EXPECT_LE(fail_count, N_LOOPS / 3); + } +} + +#define N_TIMERS 10000 + +UCS_TEST_F(twheel, precision_multi) { + std::vector t(N_TIMERS); + ucs_time_t start, now, eps; + + UCS_TEST_SKIP; // Test is broken + + init_timerv(&t[0], N_TIMERS); + for (int i = 0; i < N_TIMERS; i++) { + set_timer_delta(&t[i], i); + add_timer(&t[i]); + } + + start = ucs_get_time(); + /* all timers were delayed by at most eps */ + eps = start - m_wheel.now; + do { + now = ucs_get_time(); + ucs_twheel_sweep(&m_wheel, now); + } while (now < start + m_wheel.res * m_wheel.num_slots); + + /* all timers should ve been triggered + * correct delta + */ + for (int i = 0; i < N_TIMERS; i++) { + EXPECT_NE(t[i].end_time, 0); + EXPECT_NEAR(t[i].total_time, t[i].d, 2 * m_wheel.res + eps); + } +} + +UCS_TEST_F(twheel, add_twice) { + struct hr_timer t; + + init_timer(&t, 0); + + set_timer_delta(&t, -1); + add_timer(&t); + + set_timer_delta(&t, -1); + EXPECT_EQ(ucs_wtimer_add(&m_wheel, &t.timer, t.d), UCS_ERR_BUSY); + do { + ucs_twheel_sweep(&m_wheel, ucs_get_time()); + } while(t.end_time == 0); +} + + +UCS_TEST_F(twheel, add_overflow) { + struct hr_timer t; + + UCS_TEST_SKIP; // Test is broken + + init_timer(&t, 0); + ::srand(::time(NULL)); + + t.total_time = 0; + set_timer_delta(&t, -2); + for (int i = 0; i < N_LOOPS; i++) { + add_timer(&t); + do { + ucs_twheel_sweep(&m_wheel, ucs_get_time()); + } while (t.end_time == 0); + } + EXPECT_NEAR(t.total_time , t.d * N_LOOPS, 4 * N_LOOPS * m_wheel.res); +} + +UCS_TEST_F(twheel, delayed_sweep) { + std::vector t(N_TIMERS); + + init_timerv(&t[0], N_TIMERS); + for (int i = 0; i < N_TIMERS; i++) { + set_timer_delta(&t[i], i); + add_timer(&t[i]); + } + + sleep(1); + + ucs_twheel_sweep(&m_wheel, ucs_get_time()); + + /* all timers should have been triggered */ + for (int i = 0; i < N_TIMERS; i++) { + EXPECT_NE(t[i].end_time, 0); + } +} +