diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2015-10-25 15:03:26 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2015-10-26 12:08:33 +0100 |
commit | 6d2c0c6e8e39e8cd7ae14a3cf27d9f2bd76749f2 (patch) | |
tree | 6736cb42dd360ce9b6477ac833bd4e23e0aae53c | |
parent | 0dcd56db5e8fef6ab9034aa9b521e010ffa283c5 (diff) |
WIPwip
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r-- | .gitignore | 30 | ||||
-rw-r--r-- | .vimrc | 4 | ||||
-rw-r--r-- | COPYING | 0 | ||||
-rw-r--r-- | Makefile.am | 107 | ||||
-rwxr-xr-x | autogen.sh | 31 | ||||
-rw-r--r-- | configure.ac | 145 | ||||
-rw-r--r-- | m4/attributes.m4 | 77 | ||||
-rw-r--r-- | src/bus1-macro.h | 629 | ||||
-rw-r--r-- | src/bus1-sys.c | 32 | ||||
-rw-r--r-- | src/bus1-sys.h | 49 | ||||
-rw-r--r-- | src/bus1ctl.c | 22 | ||||
-rw-r--r-- | src/test-macro.c | 392 | ||||
-rw-r--r-- | src/test-perf.c | 230 |
13 files changed, 1748 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08bc506 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +*.a +*.la +*.lo +*.log +*.o +*.stamp +*.swp +*.trs +*~ + +.config.args +.deps/ +.dirstamp +.libs/ +/*.tar.xz +Makefile +Makefile.in +autom4te.cache/ +aclocal.m4 +build-aux/ +bus1ctl +config.h +config.h.in +config.log +config.status +configure +m4/ +stamp-* +test-macro +test-perf @@ -0,0 +1,4 @@ +" 'set exrc' in ~/.vimrc will read .vimrc from the current directory +set tabstop=8 +set shiftwidth=8 +set expandtab diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8923add --- /dev/null +++ b/Makefile.am @@ -0,0 +1,107 @@ +# ------------------------------------------------------------------------------ +# main + +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +AM_MAKEFLAGS = --no-print-directory +AUTOMAKE_OPTIONS = color-tests parallel-tests + +GCC_COLORS ?= 'ooh, shiny!' +export GCC_COLORS + +SUBDIRS = . + +# remove targets if the command fails +.DELETE_ON_ERROR: + +# keep intermediate files +.SECONDARY: + +# Keep the test-suite.log and Makefile around at all times +.PRECIOUS: $(TEST_SUITE_LOG) Makefile + +CLEANFILES = $(BUILT_SOURCES) +DISTCLEANFILES = +EXTRA_DIST = +BUILT_SOURCES = +lib_LIBRARIES = +noinst_LIBRARIES = +bin_PROGRAMS = +noinst_PROGRAMS = +check_PROGRAMS = +TESTS = +default_tests = + +AM_CPPFLAGS = \ + -include $(top_builddir)/config.h \ + -I $(top_srcdir)/src \ + -I $(top_builddir)/src \ + $(OUR_CPPFLAGS) + +AM_CFLAGS = $(OUR_CFLAGS) +AM_LDFLAGS = $(OUR_LDFLAGS) + +# ------------------------------------------------------------------------------ +# libbus1 +# XXX: for now static, should be made dynamic later on + +noinst_LIBRARIES += \ + libbus1.a + +libbus1_a_SOURCES = \ + src/bus1-macro.h \ + src/bus1-sys.c \ + src/bus1-sys.h + +libbus1_a_CFLAGS = \ + $(AM_CFLAGS) + +# ------------------------------------------------------------------------------ +# bus1ctl + +bin_PROGRAMS += \ + bus1ctl + +bus1ctl_SOURCES = \ + src/bus1ctl.c + +bus1ctl_CFLAGS = \ + $(AM_CFLAGS) + +bus1ctl_LDADD = \ + libbus1.a + +# ------------------------------------------------------------------------------ +# test-macro + +default_tests += \ + test-macro + +test_macro_SOURCES = \ + src/test-macro.c + +test_macro_CFLAGS = \ + $(AM_CFLAGS) + +test_macro_LDADD = \ + libbus1.a + +# ------------------------------------------------------------------------------ +# test-perf + +default_tests += \ + test-perf + +test_perf_SOURCES = \ + src/test-perf.c + +test_perf_CFLAGS = \ + $(AM_CFLAGS) + +test_perf_LDADD = \ + libbus1.a + +# ------------------------------------------------------------------------------ +# test suite + +check_PROGRAMS += $(default_tests) +TESTS += $(default_tests) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d969f8d --- /dev/null +++ b/autogen.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +oldpwd=$(pwd) +topdir=$(dirname $0) +cd $topdir + +autoreconf --force --install --symlink + +if [ -f "$topdir/.config.args" ]; then + args="$args $(cat $topdir/.config.args)" +fi + +cd $oldpwd + +if [ "x$1" = "xc" ]; then + $topdir/configure --enable-debug $args + make clean +elif [ "x$1" = "xl" ]; then + $topdir/configure CC=clang $args + make clean +else + echo + echo "----------------------------------------------------------------" + echo "Initialized build system. For a common configuration please run:" + echo "----------------------------------------------------------------" + echo + echo "$topdir/configure $args" + echo +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..5f9aaa1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,145 @@ +# ------------------------------------------------------------------------------ +# main + +AC_PREREQ([2.64]) + +AC_INIT([libbus1], + [1], + [http://www.github.com/bus1/libbus1], + [libbus1], + [http://www.github.com/bus1/libbus1]) + +AC_CONFIG_SRCDIR([src/bus1-macro.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) + +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE +AM_MAINTAINER_MODE([enable]) +AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects parallel-tests]) +AM_SILENT_RULES([yes]) +AC_CANONICAL_HOST +AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.]) + +AC_PROG_MKDIR_P +AC_PROG_LN_S +AC_PROG_SED +AC_PROG_GREP +AC_PROG_AWK +AC_PROG_RANLIB +AC_PROG_CC_C99 +AM_PROG_AR + +AC_PATH_PROG([M4], [m4]) +AC_PATH_PROG([XSLTPROC], [xsltproc]) + +AS_IF([! ln --relative --help > /dev/null 2>&1], [AC_MSG_ERROR([*** ln doesn't support --relative ***])]) + +# ------------------------------------------------------------------------------ +# arguments + +AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debug options])) + +# ------------------------------------------------------------------------------ +# toolchain + +CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\ + -pipe \ + -Wall \ + -Wextra \ + -Wno-inline \ + -Wundef \ + "-Wformat=2 -Wformat-security -Wformat-nonliteral" \ + -Wlogical-op \ + -Wsign-compare \ + -Wmissing-include-dirs \ + -Wold-style-definition \ + -Wpointer-arith \ + -Winit-self \ + -Wdeclaration-after-statement \ + -Wfloat-equal \ + -Wsuggest-attribute=noreturn \ + -Wmissing-prototypes \ + -Wstrict-prototypes \ + -Wredundant-decls \ + -Wmissing-declarations \ + -Wmissing-noreturn \ + -Wshadow \ + -Wendif-labels \ + -Wstrict-aliasing=2 \ + -Wwrite-strings \ + -Wno-long-long \ + -Wno-overlength-strings \ + -Wno-unused-parameter \ + -Wno-missing-field-initializers \ + -Wno-unused-result \ + -Werror=overflow \ + -Wdate-time \ + -Wnested-externs \ + -ffast-math \ + -fno-common \ + -fdiagnostics-show-option \ + -fno-strict-aliasing \ + -fvisibility=hidden \ + -ffunction-sections \ + -fdata-sections \ + -fstack-protector \ + -fstack-protector-strong \ + -fPIE \ + --param=ssp-buffer-size=4]) + +AS_CASE([$CC], [*clang*], + [CC_CHECK_FLAGS_APPEND([with_cppflags], [CPPFLAGS], [ \ + -Wno-typedef-redefinition \ + -Wno-gnu-variable-sized-type-not-at-end \ + ])]) + +CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\ + -Wl,--as-needed \ + -Wl,--no-undefined \ + -Wl,--gc-sections \ + -Wl,-z,relro \ + -Wl,-z,now \ + -pie]) + +AS_IF([test "x$enable_debug" = "xyes"], + [ + CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [ \ + -g \ + -O0 \ + -ftrapv]) + ], [ + CC_CHECK_FLAGS_APPEND([with_cppflags], [CPPFLAGS], [\ + -Wp,-D_FORTIFY_SOURCE=2]) + ]) + +AC_SUBST([OUR_CFLAGS], "$with_cflags") +AC_SUBST([OUR_CPPFLAGS], "$with_cppflags") +AC_SUBST([OUR_LDFLAGS], "$with_ldflags") + +# ------------------------------------------------------------------------------ +# system features + +# This makes sure pkg.m4 is available. +m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) + +# ------------------------------------------------------------------------------ +# report + +AC_CONFIG_FILES([Makefile]) + +AC_OUTPUT +AC_MSG_RESULT([ + $PACKAGE_NAME $VERSION + debug: ${enable_debug} + + prefix: ${prefix} + exec_prefix: ${exec_prefix} + includedir: ${includedir} + libdir: ${libdir} + + CFLAGS: ${OUR_CFLAGS} ${CFLAGS} + CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS} + LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS} +]) diff --git a/m4/attributes.m4 b/m4/attributes.m4 new file mode 100644 index 0000000..e145f49 --- /dev/null +++ b/m4/attributes.m4 @@ -0,0 +1,77 @@ +dnl Macros to check the presence of generic (non-typed) symbols. +dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com> +dnl Copyright (c) 2006-2008 xine project +dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com> +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +dnl 02110-1301, USA. +dnl +dnl As a special exception, the copyright owners of the +dnl macro gives unlimited permission to copy, distribute and modify the +dnl configure scripts that are the output of Autoconf when processing the +dnl Macro. You need not follow the terms of the GNU General Public +dnl License when using or distributing such scripts, even though portions +dnl of the text of the Macro appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that +dnl constitutes the Autoconf Macro. +dnl +dnl This special exception to the GPL applies to versions of the +dnl Autoconf Macro released by this project. When you make and +dnl distribute a modified version of the Autoconf Macro, you may extend +dnl this special exception to the GPL to apply to your modified version as +dnl well. + +dnl Check if FLAG in ENV-VAR is supported by compiler and append it +dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to +dnl -W* as gcc cannot test for negated warnings. +dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG]) + +AC_DEFUN([CC_CHECK_FLAG_APPEND], [ + AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2], + AS_TR_SH([cc_cv_$2_$3]), + [eval "AS_TR_SH([cc_save_$2])='${$2}'" + eval "AS_TR_SH([$2])='-Werror `echo "$3" | sed 's/^-Wno-/-W/'`'" + AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; } ])], + [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"], + [eval "AS_TR_SH([cc_cv_$2_$3])='no'"]) + eval "AS_TR_SH([$2])='$cc_save_$2'"]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes], + [eval "$1='${$1} $3'"]) +]) + +dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2]) +AC_DEFUN([CC_CHECK_FLAGS_APPEND], [ + for flag in $3; do + CC_CHECK_FLAG_APPEND($1, $2, $flag) + done +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +AC_DEFUN([CC_CHECK_LDFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" + AC_LINK_IFELSE([int main() { return 1; }], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + LDFLAGS="$ac_save_LDFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) diff --git a/src/bus1-macro.h b/src/bus1-macro.h new file mode 100644 index 0000000..1185326 --- /dev/null +++ b/src/bus1-macro.h @@ -0,0 +1,629 @@ +#pragma once + +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +/* + * Macros + * XXX: Add description + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * We require: + * sizeof(void*) == sizeof(long) + * sizeof(long) == 4 || sizeof(long) == 8 + * sizeof(int) == 4 + * The linux kernel requires the same from the toolchain, so this should work + * just fine. + */ +#if __SIZEOF_POINTER__ != __SIZEOF_LONG__ +# error "sizeof(void*) != sizeof(long)" +#elif __SIZEOF_LONG__ != 4 && __SIZEOF_LONG__ != 8 +# error "sizeof(long) != 4 && sizeof(long) != 8" +#elif __SIZEOF_INT__ != 4 +# error "sizeof(int) != 4" +#endif + +/* + * Shortcuts for gcc attributes. See GCC manual for details. We do not prefix + * them as they're 1-on-1 mappings to the GCC equivalents. + */ +#define _alloc_(...) __attribute__((alloc_size(__VA_ARGS__))) +#define _cleanup_(_x) __attribute__((cleanup(_x))) +#define _const_ __attribute__((const)) +#define _deprecated_ __attribute__((deprecated)) +#define _hidden_ __attribute__((visibility("hidden"))) +#define _likely_(_x) (__builtin_expect(!!(_x), 1)) +#define _malloc_ __attribute__((malloc)) +#define _packed_ __attribute__((packed)) +#define _printf_(_a, _b) __attribute__((format (printf, _a, _b))) +#define _public_ __attribute__((visibility("default"))) +#define _pure_ __attribute__((pure)) +#define _sentinel_ __attribute__((sentinel)) +#define _unlikely_(_x) (__builtin_expect(!!(_x), 0)) +#define _unused_ __attribute__((unused)) +#define _weak_ __attribute__((weak)) +#define _weakref_(_x) __attribute__((weakref(#_x))) + +/** + * B1_TYPE_MATCH() - match two variables/types for unqualified equality + * @_a: first variable/type + * @_b: second variable/type + * + * Compare two types, or types of two variables, for equality. Note that type + * qualifiers are not respected by this comparison. Hence, only the actual + * underlying types are compared. + * + * Return: 1 if both unqualified types are equal, 0 if not. + */ +#define B1_TYPE_MATCH(_a, _b) __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b)) + +/** + * B1_TYPE_IS_ARRAY() - evaluate whether given variable is of an array type + * @_a: variable/type to evaluate + * + * This function checks whether a given variable is an array type. Note that + * the passed argument must either be an array or pointer, otherwise, this will + * generate a syntax error. + * + * Note that "&a[0]" degrades an array to a pointer, and as such compares + * unequal to "a" if it is an array. This is unique to array types. + * + * Return: 1 if it is an array type, 0 if not. + */ +#define B1_TYPE_IS_ARRAY(_a) (!B1_TYPE_MATCH(__typeof__(_a), &(*(__typeof__(_a)*)0)[0])) + +/** + * B1_CC_IF() - conditional expression at compile time + * @_cond: condition + * @_if: if-clause + * @_else: else-clause + * + * This is a compile-time if-else-statement. Depending on whether the constant + * expression @_cond is true or false, this evaluates to the passed clause. The + * other clause is *not* evaluated, however, it may be checked for syntax + * errors and *constant* expressions are evaluated. + * + * Return: Evaluates to either if-clause or else-clause, depending on whether + * the condition is true. The other clause is *not* evaluated. + */ +#define B1_CC_IF(_cond, _if, _else) __builtin_choose_expr(!!(_cond), _if, _else) + +/** + * B1_CC_IS_CONST() - check whether a value is known at compile time + * @_expr: expression + * + * This checks whether the value of @_expr is known at compile time. Note that + * a negative result does not mean that it is *NOT* known. However, it means + * that it cannot be guaranteed to be constant at compile time. Hence, false + * negatives are possible. + * + * This macro *always* evaluates to a constant expression, regardless whether + * the passed expression is constant. + * + * The passed in expression is *never* evaluated. Hence, it can safely be used + * in combination with B1_CC_IF() to avoid multiple evaluations of macro + * parameters. + * + * Return: 1 if constant, 0 if not. + */ +#define B1_CC_IS_CONST(_expr) __builtin_constant_p(_expr) + +/** + * B1_CC_ASSERT_MSG() - compile time assertion + * @_cond: condition + * @_msg: message to make the compiler print + * + * This is a compile-time assertion that can be used in any (constant) + * expression. If @_cond evalutes to true, this is equivalent to a void + * expression. If @_cond is false, this will cause a compiler error and print + * @_msg into the compile log. + * + * XXX: Find some gcc hack to print @_msg while keeping the macro a constant + * expression. + * + * Return: This macro evaluates to a void expression. + */ +#define B1_CC_ASSERT_MSG(_cond, _msg) ((void)B1_CC_ASSERT1_MSG((_cond), _msg)) + +/** + * B1_CC_ASSERT1_MSG() - compile time assertion + * @_cond: condition + * @_msg: message to make the compiler print + * + * This is the same as B1_CC_ASSERT_MSG(), but evaluates to constant 1. + * + * Return: This macro evaluates to constant 1. + */ +#define B1_CC_ASSERT1_MSG(_cond, _msg) (sizeof(int[!(_cond) * -1]) * 0 + 1) + +/** + * B1_CC_ASSERT() - compile time assertion + * @_cond: condition + * + * Same as B1_CC_ASSERT_MSG() but prints the condition as error message. + * + * Return: This macro evaluates to a void expression. + */ +#define B1_CC_ASSERT(_cond) ((void)B1_CC_ASSERT1(_cond)) + +/** + * B1_CC_ASSERT1() - compile time assertion + * @_cond: condition + * + * This is the same as B1_CC_ASSERT(), but evaluates to constant 1. + * + * Return: This macro evaluates to constant 1. + */ +#define B1_CC_ASSERT1(_cond) B1_CC_ASSERT1_MSG((_cond), #_cond) + +/** + * B1_CC_ASSERT_TO() - compile time assertion with explicit return value + * @_cond: condition to assert + * @_expr: expression to yield + * + * This is equivalent to B1_CC_ASSERT1(_cond), but yields a return value of + * @_expr, rather than constant 1. + * + * In case the compile-time assertion is false, this causes a compile-time + * error and *also* evaluates as a void expression (and as such usually causes + * a followup compile time error). + * + * Note that usually you'd do something like: + * (ASSERT(cond), expr) + * thus using the comma-operator to yield a specific value. However, + * suprisingly STD-C does *not* define the comma operator as constant + * expression. Hence, we have to use B1_CC_IF() to yield the same result. + * + * Return: This macro evaluates to @_expr. + */ +#define B1_CC_ASSERT_TO(_cond, _expr) B1_CC_IF(B1_CC_ASSERT1(_cond), (_expr), ((void)0)) + +/** + * B1_CC_STATIC_ASSERT() - static compile time assertion + * @_cond: condition + * + * This is equivalent to B1_CC_ASSERT(), but evaluates to a statement instead + * of an expression. This allows usage in file-context, where STD-C does not + * allow plain expressions. If you need a custom compiler message to print, use + * static_assert() directly (it's STD-C11). + * + * Return: This macro evaluates to a statement. + */ +#define B1_CC_STATIC_ASSERT(_cond) static_assert((_cond), #_cond) + +/** + * B1_CC_STRINGIFY() - stringify a token, but evaluate it first + * @_x: token to evaluate and stringify + * + * Return: Evaluates to a constant string literal + */ +#define B1_CC_STRINGIFY(_x) B1_INTERNAL_CC_STRINGIFY(_x) +#define B1_INTERNAL_CC_STRINGIFY(_x) #_x + +/** + * B1_CC_CONCATENATE() - concatenate two tokens, but evaluate them first + * @_x: first token + * @_y: second token + * + * Return: Evaluates to a constant identifier + */ +#define B1_CC_CONCATENATE(_x, _y) B1_INTERNAL_CC_CONCATENATE(_x, _y) +#define B1_INTERNAL_CC_CONCATENATE(_x, _y) _x ## _y + +/** + * B1_CC_ARRAY_SIZE() - calculate number of array elements at compile time + * @_x: array to calculate size of + * + * Return: Evaluates to a constant integer expression + */ +#define B1_CC_ARRAY_SIZE(_x) B1_CC_ASSERT_TO(B1_TYPE_IS_ARRAY(_x), sizeof(_x) / sizeof((_x)[0])) + +/** + * B1_CC_DECIMAL_MAX() - calculate maximum length of the decimal + * representation of an integer + * @_type: integer variable/type + * + * This calculates the bytes required for the decimal representation of an + * integer of the given type. It accounts for a possible +/- prefix, but it + * does *NOT* include the trailing terminating zero byte. + * + * Return: Evaluates to a constant integer expression + */ +#define B1_CC_DECIMAL_MAX(_type) \ + _Generic((_type){ 0 }, \ + char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + signed char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + unsigned char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + signed short: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + unsigned short: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + signed int: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + unsigned int: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + signed long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + unsigned long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + signed long long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \ + unsigned long long: B1_INTERNAL_CC_DECIMAL_MAX(_type)) +#define B1_INTERNAL_CC_DECIMAL_MAX(_type) \ + (1 + (sizeof(_type) <= 1 ? 3 : \ + sizeof(_type) <= 2 ? 5 : \ + sizeof(_type) <= 4 ? 10 : \ + B1_CC_ASSERT_TO(sizeof(_type) <= 8, 20))) + +/** + * B1_CC_UNIQUE - generate unique compile-time integer + * + * This evaluates to a unique compile-time integer. Each occurrence of this + * macro in the *preprocessed* C-code resolves to a different, unique integer. + * Internally, it uses the __COUNTER__ gcc extension, and as such all + * occurrences generate a dense set of integers. + * + * Return: This evaluates to an integer literal + */ +#define B1_CC_UNIQUE __COUNTER__ + +/** + * B1_VAR() - generate unique variable name + * @_x: name of variable + * @_uniq: unique prefix, usually provided by @B1_CC_UNIQUE + * + * This macro shall be used to generate unique variable names, that will not be + * shadowed by recursive macro invocations. It is effectively a + * B1_CC_CONCATENATE of both arguments, but also provides a globally separated + * prefix and makes the code better readable. + * + * This helper may be used by macro implementations that might reasonable well + * be called in a stacked fasion, like: + * b1_max(foo, b1_max(bar, baz)) + * Such a stacked call of b1_max() might cause compiler warnings of shadowed + * variables in the definition of b1_max(). By using B1_VAR(), such warnings + * can be silenced as each evaluation of b1_max() uses unique variable names. + * + * Return: This evaluates to a constant identifier + */ +#define B1_VAR(_x, _uniq) B1_CC_CONCATENATE(b1_var_unique_prefix_, B1_CC_CONCATENATE(_uniq, _x)) + +/** + * container_of() - cast a member of a structure out to the containing structure + * @_ptr: pointer to the member or NULL + * @_type: type of the container struct this is embedded in + * @_member: name of the member within the struct + */ +#define b1_container_of(_ptr, _type, _member) b1_internal_container_of(B1_CC_UNIQUE, (_ptr), _type, _member) +#define b1_internal_container_of(_uniq, _ptr, _type, _member) \ + __extension__ ({ \ + const __typeof__( ((_type*)0)->_member ) *B1_VAR(A, _uniq) = (_ptr); \ + (_ptr) ? (_type*)( (char*)B1_VAR(A, _uniq) - offsetof(_type, _member) ) : NULL; \ + }) + +/** + * b1_max() - compute maximum of two values + * @_a: value A + * @_b: value B + * + * Calculate the maximum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * Return: Maximum of both values is returned. + */ +#define b1_max(_a, _b) b1_internal_max(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b)) +#define b1_internal_max(_aq, _a, _bq, _b) \ + B1_CC_IF( \ + (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \ + ((_a) > (_b) ? (_a) : (_b)), \ + __extension__ ({ \ + const __auto_type B1_VAR(A, _aq) = (_a); \ + const __auto_type B1_VAR(B, _bq) = (_b); \ + B1_VAR(A, _aq) > B1_VAR(B, _bq) ? B1_VAR(A, _aq) : B1_VAR(B, _bq); \ + })) + +/** + * b1_min() - compute minimum of two values + * @_a: value A + * @_b: value B + * + * Calculate the minimum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * Return: Minimum of both values is returned. + */ +#define b1_min(_a, _b) b1_internal_min(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b)) +#define b1_internal_min(_aq, _a, _bq, _b) \ + B1_CC_IF( \ + (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \ + ((_a) < (_b) ? (_a) : (_b)), \ + __extension__ ({ \ + const __auto_type B1_VAR(A, _aq) = (_a); \ + const __auto_type B1_VAR(B, _bq) = (_b); \ + B1_VAR(A, _aq) < B1_VAR(B, _bq) ? B1_VAR(A, _aq) : B1_VAR(B, _bq); \ + })) + +/** + * b1_less_by() - calculate clamped difference of two values + * @_a: minuend + * @_b: subtrahend + * + * Calculate [_a - _b], but clamp the result to 0. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * Return: This computes [_a - _b], if [_a > _b]. Otherwise, 0 is returned. + */ +#define b1_less_by(_a, _b) b1_internal_less_by(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b)) +#define b1_internal_less_by(_aq, _a, _bq, _b) \ + B1_CC_IF( \ + (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \ + ((_a) > (_b) ? ((_a) - (_b)) : 0), \ + __extension__ ({ \ + const __auto_type B1_VAR(A, _aq) = (_a); \ + const __auto_type B1_VAR(B, _bq) = (_b); \ + B1_VAR(A, _aq) > B1_VAR(B, _bq) ? B1_VAR(A, _aq) - B1_VAR(B, _bq) : 0; \ + })) + +/** + * b1_clamp() - clamp value to lower and upper boundary + * @_x: value to clamp + * @_low: lower boundary + * @_high: higher boundary + * + * This clamps @_x to the lower and higher bounds given as @_low and @_high. + * All arguments are evaluated exactly once, and yield a constant expression if + * all arguments are constant as well. + * + * Return: Clamped integer value. + */ +#define b1_clamp(_x, _low, _high) b1_internal_clamp(B1_CC_UNIQUE, (_x), B1_CC_UNIQUE, (_low), B1_CC_UNIQUE, (_high)) +#define b1_internal_clamp(_xq, _x, _lowq, _low, _highq, _high) \ + B1_CC_IF( \ + (B1_CC_IS_CONST(_x) && B1_CC_IS_CONST(_low) && B1_CC_IS_CONST(_high)), \ + ((_x) > (_high) ? \ + (_high) : \ + (_x) < (_low) ? \ + (_low) : \ + (_x)), \ + __extension__ ({ \ + const __auto_type B1_VAR(X, _xq) = (_x); \ + const __auto_type B1_VAR(LOW, _lowq) = (_low); \ + const __auto_type B1_VAR(HIGH, _highq) = (_high); \ + B1_VAR(X, _xq) > B1_VAR(HIGH, _highq) ? \ + B1_VAR(HIGH, _highq) : \ + B1_VAR(X, _xq) < B1_VAR(LOW, _lowq) ? \ + B1_VAR(LOW, _lowq) : \ + B1_VAR(X, _xq); \ + })) + +/** + * b1_negative_errno() - return negative errno + * + * This helper should be used to shut up gcc if you know 'errno' is valid (ie., + * errno is > 0). Instead of "return -errno;", use + * "return b1_negative_errno();" It will suppress bogus gcc warnings in case + * it assumes 'errno' might be 0 (or <0) and thus the caller's error-handling + * might not be triggered. + * + * This helper should be avoided whenever possible. However, occasionally we + * really want to shut up gcc (especially with static/inline functions). In + * those cases, gcc usually cannot deduce that some error paths are guaranteed + * to be taken. Hence, making the return value explicit allows gcc to better + * optimize the code. + * + * Note that you really should never use this helper to work around broken libc + * calls or syscalls, not setting 'errno' correctly. + * + * Return: Negative error code is returned. + */ +static inline int b1_negative_errno(void) { + return _likely_(errno > 0) ? -errno : -EINVAL; +} + +/** + * b1_math_clz() - count leading zeroes + * @_val: value to count leading zeroes of + * + * This counts the leading zeroes of the binary representation of @_val. Note + * that @_val must be of an integer type greater than, or equal to, 'unsigned + * int'. Also note that a value of 0 produces an undefined result (see your CPU + * instruction manual for details why). + * + * This macro evaluates the argument exactly once, and if the input is + * constant, it also evaluates to a constant expression. + * + * Note that this macro calculates the number of leading zeroes within the + * scope of the integer type of @_val. That is, if the input is a 32bit type + * with value 1, it yields 31. But if it is a 64bit type with the same value 1, + * it yields 63. + * + * Return: Evaluates to an 'int', the number of leading zeroes. + */ +#define b1_math_clz(_val) \ + _Generic((_val), \ + unsigned int: __builtin_clz(_val), \ + unsigned long: __builtin_clzl(_val), \ + unsigned long long: __builtin_clzll(_val)) + +/** + * b1_math_div_round_up() - calculate integer quotient but round up + * @_x: dividend + * @_y: divisor + * + * Calculates [x / y] but rounds up the result to the next integer. All + * arguments are evaluated exactly once, and yield a constant expression if all + * arguments are constant. + * + * Note: + * [(x + y - 1) / y] suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on most CPUs a division returns both the + * quotient and the remainder, so both should be equally fast. Furthermore, if + * the divisor is a power of two, the compiler will optimize it, anyway. + * + * Return: The quotient is returned. + */ +#define b1_math_div_round_up(_x, _y) b1_internal_math_div_round_up(B1_CC_UNIQUE, (_x), B1_CC_UNIQUE, (_y)) +#define b1_internal_math_div_round_up(_xq, _x, _yq, _y) \ + B1_CC_IF( \ + (B1_CC_IS_CONST(_x) && B1_CC_IS_CONST(_y)), \ + ((_x) / (_y) + !!((_x) % (_y))), \ + __extension__ ({ \ + const __auto_type B1_VAR(X, _xq) = (_x); \ + const __auto_type B1_VAR(Y, _yq) = (_y); \ + (B1_VAR(X, _xq) / B1_VAR(Y, _yq) + \ + !!(B1_VAR(X, _xq) % B1_VAR(Y, _yq))); \ + })) + +/** + * b1_math_align_to() - align value to + * @_val: value to align + * @_to: align to multiple of this + * + * This aligns @_val to a multiple of @_to. If @_val is already a multiple of + * @_to, @_val is returned unchanged. This function operates within the + * boundaries of the type of @_val and @_to. Make sure to cast them if needed. + * + * The arguments of this macro are evaluated exactly once. If both arguments + * are a constant expression, this also yields a constant return value. + * + * Note that @_to must be a power of 2. In case @_to is a constant expression, + * this macro places a compile-time assertion on the popcount of @_to, to + * verify it is a power of 2. + * + * Return: @_val aligned to a multiple of @_to + */ +#define b1_math_align_to(_val, _to) b1_internal_math_align_to((_val), B1_CC_UNIQUE, (_to)) +#define b1_internal_math_align_to(_val, _toq, _to) \ + B1_CC_IF( \ + B1_CC_IS_CONST(_to), \ + B1_CC_ASSERT_TO(__builtin_popcountll(B1_CC_IF(B1_CC_IS_CONST(_to), (_to), 1)) == 1, \ + (((_val) + (_to) - 1) & ~((_to) - 1))), \ + __extension__ ({ \ + const __auto_type B1_VAR(to, _toq) = (_to); \ + ((_val) + B1_VAR(to, _toq) - 1) & ~(B1_VAR(to, _toq) - 1); \ + })) + +/** + * b1_math_align() - align to native size + * @_val: value to align + * + * This is the same as b1_math_align_to((_val), __SIZEOF_POINTER__). + * + * Return: @_val aligned to the native size + */ +#define b1_math_align(_val) b1_math_align_to((_val), __SIZEOF_POINTER__) + +/** + * b1_math_align8() - align value to multiple of 8 + * @_val: value to align + * + * This is the same as b1_math_align_to((_val), 8). + * + * Return: @_val aligned to a multiple of 8. + */ +#define b1_math_align8(_val) b1_math_align_to((_val), 8) + +/** + * b1_math_align_power2() - align value to next power of 2 + * @_val: value to align + * + * This aligns @_val to the next higher power of 2. If it already is a power of + * 2, the value is returned unchanged. 0 is treated as power of 2 (so 0 yields + * 0). Furthermore, on overflow, this yields 0 as well. + * + * Note that this always operates within the bounds of the type of @_val. + * + * Return: @_val aligned to the next higher power of 2 + */ +#define b1_math_align_power2(_val) b1_internal_math_align_power2(B1_CC_UNIQUE, (_val)) +#define b1_internal_math_align_power2(_vq, _v) \ + __extension__ ({ \ + __auto_type B1_VAR(v, _vq) = (_v); \ + /* cannot use ?: as gcc cannot do const-folding then (apparently..) */ \ + if (B1_VAR(v, _vq) == 1) /* clz(0) is undefined */ \ + B1_VAR(v, _vq) = 1; \ + else if (b1_math_clz(B1_VAR(v, _vq) - 1) < 1) /* shift overflow is undefined */ \ + B1_VAR(v, _vq) = 0; \ + else \ + B1_VAR(v, _vq) = ((__typeof__(B1_VAR(v, _vq)))1) << \ + (sizeof(B1_VAR(v, _vq)) * 8 - b1_math_clz(B1_VAR(v, _vq) - 1)); \ + B1_VAR(v, _vq); \ + }) + +/* + * XXX: move to separate header + * XXX: write test suite + * + * String helpers + */ + +#include <string.h> + +_pure_ static inline bool b1_str_equal(const char *a, const char *b) { + return (!a || !b) ? (a == b) : !strcmp(a, b); +} + +_pure_ static inline char *b1_str_prefix(const char *str, const char *prefix) { + size_t l = strlen(prefix); + return !strncmp(str, prefix, l) ? (char *)str + l : NULL; +} + +/* + * XXX: move to separate header + * XXX: write test suite + * + * Micro-second helpers + */ + +#include <sys/time.h> +#include <time.h> + +/* stores up to 584,942.417355 years */ +typedef uint64_t b1_usec; + +#define b1_usec_from_nsec(_nsec) ((_nsec) / UINT64_C(1000)) +#define b1_usec_from_msec(_msec) ((_msec) * UINT64_C(1000)) +#define b1_usec_from_sec(_sec) b1_usec_from_msec((_sec) * UINT64_C(1000)) +#define b1_usec_from_timespec(_ts) (b1_usec_from_sec((_ts)->tv_sec) + b1_usec_from_nsec((_ts)->tv_nsec)) +#define b1_usec_from_timeval(_tv) (b1_usec_from_sec((_tv)->tv_sec) + (_tv)->tv_usec) + +static inline b1_usec b1_usec_from_clock(clockid_t clock) { + struct timespec ts; + int r; + + r = clock_gettime(clock, &ts); + assert(r >= 0); + return b1_usec_from_timespec(&ts); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/bus1-sys.c b/src/bus1-sys.c new file mode 100644 index 0000000..ebeeba1 --- /dev/null +++ b/src/bus1-sys.c @@ -0,0 +1,32 @@ +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <linux/memfd.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> +#include "bus1-macro.h" +#include "bus1-sys.h" + +int b1_sys_memfd_create(const char *name, unsigned int flags) { +#ifndef __NR_memfd_create + static_assert(false, "System lacks memfd_create(2) syscall"); +#endif + return syscall(__NR_memfd_create, name, flags); +} diff --git a/src/bus1-sys.h b/src/bus1-sys.h new file mode 100644 index 0000000..b50ec5d --- /dev/null +++ b/src/bus1-sys.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +/* + * Syscall helpers + * XXX: Add description + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include "bus1-macro.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * b1_sys_memfd_create() - wrapper for memfd_create(2) syscall + * @name: name for memfd inode + * @flags: memfd flags + * + * This is a wrapper for the memfd_create(2) syscall. Currently, no user-space + * wrapper is exported by any libc. + * + * Return: New memfd file-descriptor on success, -1 on failure. + */ +int b1_sys_memfd_create(const char *name, unsigned int flags); + +#ifdef __cplusplus +} +#endif diff --git a/src/bus1ctl.c b/src/bus1ctl.c new file mode 100644 index 0000000..3f35850 --- /dev/null +++ b/src/bus1ctl.c @@ -0,0 +1,22 @@ +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> + +int main(int argc, char **argv) { + return 0; +} diff --git a/src/test-macro.c b/src/test-macro.c new file mode 100644 index 0000000..7d76e86 --- /dev/null +++ b/src/test-macro.c @@ -0,0 +1,392 @@ +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "bus1-macro.h" + +/* test B1_TYPE_* helpers */ +static void test_type(void) { + int foo, bar, array[16] = {}; + long baz; + + /* test type-matching with mixed explicit types and variables */ + B1_CC_ASSERT(B1_TYPE_MATCH(foo, int)); + B1_CC_ASSERT(B1_TYPE_MATCH(int, foo)); + B1_CC_ASSERT(B1_TYPE_MATCH(int, int)); + B1_CC_ASSERT(B1_TYPE_MATCH(foo, foo)); + B1_CC_ASSERT(B1_TYPE_MATCH(foo, bar)); + B1_CC_ASSERT(B1_TYPE_MATCH(bar, foo)); + B1_CC_ASSERT(B1_TYPE_MATCH(baz, long)); + B1_CC_ASSERT(!B1_TYPE_MATCH(bar, long)); + B1_CC_ASSERT(!B1_TYPE_MATCH(long, bar)); + B1_CC_ASSERT(!B1_TYPE_MATCH(int, long)); + B1_CC_ASSERT(!B1_TYPE_MATCH(int, baz)); + B1_CC_ASSERT(!B1_TYPE_MATCH(baz, int)); + B1_CC_ASSERT(!B1_TYPE_MATCH(foo, baz)); + + /* test type-matching with qualifiers */ + B1_CC_ASSERT(B1_TYPE_MATCH(int, const int)); + B1_CC_ASSERT(B1_TYPE_MATCH(const int, const int)); + B1_CC_ASSERT(B1_TYPE_MATCH(const int, int)); + B1_CC_ASSERT(!B1_TYPE_MATCH(int, int*)); + B1_CC_ASSERT(!B1_TYPE_MATCH(int[], int*)); + B1_CC_ASSERT(!B1_TYPE_MATCH(int*, int**)); + B1_CC_ASSERT(B1_TYPE_MATCH(int*, int*)); + B1_CC_ASSERT(B1_TYPE_MATCH(int**, int**)); + B1_CC_ASSERT(B1_TYPE_MATCH(int[], int[])); + + /* test array verification */ + B1_CC_ASSERT(B1_TYPE_IS_ARRAY(int[])); + B1_CC_ASSERT(B1_TYPE_IS_ARRAY(int*[])); + B1_CC_ASSERT(!B1_TYPE_IS_ARRAY(int*)); + B1_CC_ASSERT(!B1_TYPE_IS_ARRAY(int**)); + + /* silence 'unused variable' warnings */ + foo = 1; + bar = 1; + baz = 1; + assert(foo == bar); + assert(foo == baz); + assert(sizeof(array) == sizeof(*array) * 16); +} + +/* test static assertions with/without messages on file-context */ +B1_CC_STATIC_ASSERT(sizeof(int) > 0); +static_assert(sizeof(int) <= sizeof(long), "Custom error message"); + +/* test B1_CC_* helpers */ +static void test_cc(int non_constant_expr) { + int foo = -16; + int bar[8]; + + /* + * Test static assertions with/without messsages on function-context. + * Those assert-helpers evaluate to a statement, and as such must be + * valid in function-context. + */ + B1_CC_STATIC_ASSERT(true); + static_assert(true, "Custom error message"); + + /* + * Test static assertion in expressions with/without messages. Those + * helpers evaluate to constant expressions, and as such can be used in + * any expression. + * We also explicitly initialize static variables to test whether the + * values are true constant expressions. + */ + foo = (B1_CC_ASSERT(true), 5); + foo = (B1_CC_ASSERT_MSG(true, "Custom error message"), foo + 1); + B1_CC_ASSERT(B1_CC_ASSERT1(8) == 1); + assert(foo == 6); + + { + /* constant expression */ + static int sub = B1_CC_ASSERT_TO(true, 16); + assert(sub == 16); + } + + /* + * Test compile-time conditions. The B1_CC_IF() macro allows evaluation + * at compile-time, and as such yields exactly one of the code-blocks + * passed to it (depending on whether the expression is true). + */ + foo = B1_CC_IF(false, foo + 0, foo + 1); + foo = B1_CC_IF(true, foo + 4, foo + 8); + assert(foo == 11); + + /* + * Test constant-expr checks. + * The B1_CC_IS_CONST() macro allows verifying whether an expression is + * constant. The return value of the macro itself is constant, and as + * such can be used for constant expressions itself. + */ + B1_CC_ASSERT(B1_CC_IS_CONST(5)); + B1_CC_ASSERT(!B1_CC_IS_CONST(non_constant_expr)); + B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_IS_CONST(non_constant_expr))); + B1_CC_ASSERT(!B1_CC_IS_CONST(foo++)); /* *NOT* evaluated */ + assert(foo == 11); + + /* + * Test stringify/concatenation helpers. Also make sure to test that + * the passed arguments are evaluated first, before they're stringified + * and/or concatenated. + */ +#define TEST_TOKEN foobar + assert(!strcmp("foobar", B1_CC_STRINGIFY(foobar))); + assert(!strcmp("foobar", B1_CC_STRINGIFY(TEST_TOKEN))); + assert(!strcmp("foobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(foo, bar)))); + assert(!strcmp("foobarfoobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(TEST_TOKEN, foobar)))); + assert(!strcmp("foobarfoobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(foobar, TEST_TOKEN)))); +#undef TEST_TOKEN + + /* + * Test array-size helper. This simply computes the number of elements + * of an array, instead of the binary size. + */ + B1_CC_ASSERT(B1_CC_ARRAY_SIZE(bar) == 8); + B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_ARRAY_SIZE(bar))); + + /* + * Test unique compile-time value. The B1_CC_UNIQUE value evaluates to + * a compile-time unique value for each time it is used. Hence, it can + * never compare equal to itself, furthermore, it's evaluated at + * compile-time, not pre-processor time! + */ +#define TEST_UNIQUE_MACRO B1_CC_UNIQUE + assert(B1_CC_UNIQUE != B1_CC_UNIQUE); + assert(TEST_UNIQUE_MACRO != TEST_UNIQUE_MACRO); + assert(B1_CC_UNIQUE != TEST_UNIQUE_MACRO); +#undef TEST_UNIQUE_MACRO + + /* + * Test decimal-representation calculator. Make sure it is + * type-independent and just uses the size of the type to calculate how + * many bytes are needed to print that integer in decimal form. Also + * verify that it is a constant expression. + */ + B1_CC_ASSERT(B1_CC_DECIMAL_MAX(int32_t) == 11); + B1_CC_ASSERT(B1_CC_DECIMAL_MAX(uint32_t) == 11); + B1_CC_ASSERT(B1_CC_DECIMAL_MAX(uint64_t) == 21); + B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_DECIMAL_MAX(int32_t))); +} + +static void test_misc(int non_constant_expr) { + int foo; + + /* + * Test b1_container_of(). We cannot test for type-safety, nor for + * other invalid uses, as they'd require negative compile-testing. + * However, we can test that the macro yields the correct values under + * normal use. + */ + { + struct foobar { + int a; + int b; + } sub = {}; + + B1_CC_ASSERT(&sub == b1_container_of(&sub.a, struct foobar, a)); + B1_CC_ASSERT(&sub == b1_container_of(&sub.b, struct foobar, b)); + } + + /* + * Test min/max macros. Especially check that macro arguments are never + * evaluated multiple times, and if both arguments are constant, the + * return value is constant as well. + */ + foo = 0; + assert(b1_max(1, 5) == 5); + assert(b1_max(-1, 5) == 5); + assert(b1_max(-1, -5) == -1); + assert(b1_max(foo++, -1) == 0); + assert(foo == 1); + assert(b1_max(foo++, foo++) > 0); + assert(foo == 3); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_max(1, 5))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_max(1, non_constant_expr))); + + { + static int sub = b1_max(1, 5); + assert(sub == 5); + } + + foo = 0; + assert(b1_min(1, 5) == 1); + assert(b1_min(-1, 5) == -1); + assert(b1_min(-1, -5) == -5); + assert(b1_min(foo++, 1) == 0); + assert(foo == 1); + assert(b1_min(foo++, foo++) > 0); + assert(foo == 3); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_min(1, 5))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_min(1, non_constant_expr))); + + { + static int sub = b1_min(1, 5); + assert(sub == 1); + } + + /* + * Test b1_less_by(), b1_clamp(). Make sure they + * evaluate arguments exactly once, and yield a constant expression, + * if all arguments are constant. + */ + foo = 8; + assert(b1_less_by(1, 5) == 0); + assert(b1_less_by(5, 1) == 4); + assert(b1_less_by(foo++, 1) == 7); + assert(foo == 9); + assert(b1_less_by(foo++, foo++) >= 0); + assert(foo == 11); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_less_by(1, 5))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_less_by(1, non_constant_expr))); + + foo = 8; + assert(b1_clamp(foo, 1, 5) == 5); + assert(b1_clamp(foo, 9, 20) == 9); + assert(b1_clamp(foo++, 1, 5) == 5); + assert(foo == 9); + assert(b1_clamp(foo++, foo++, foo++) >= 0); + assert(foo == 12); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_clamp(0, 1, 5))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_clamp(1, 0, non_constant_expr))); + + /* + * Test b1_negative_errno(). Do this by writing a code-path where gcc + * couldn't assume that 'errno' is negative, but the helper does + * provide such clue. + */ + errno = ENOSYS; + { + int uninitialized, sub = b1_negative_errno(); + + if (sub < 0) + assert(sub == -errno); + else + assert(uninitialized == 0); /* never evaluated */ + } +} + +static void test_math(int non_constant_expr) { + int i, j, foo; + + /* + * Count Leading Zeroes: The b1_math_clz() macro is a type-generic + * variant of clz(). It counts leading zeroes of an integer. The result + * highly depends on the integer-width of the input. Make sure it + * selects the correct implementation. + * Also note: clz(0) is undefined! + */ + B1_CC_ASSERT(b1_math_clz(UINT32_C(1)) == 31); + B1_CC_ASSERT(b1_math_clz(UINT64_C(1)) == 63); + + B1_CC_ASSERT(b1_math_clz(UINT32_C(-1)) == 0); + B1_CC_ASSERT(b1_math_clz(UINT32_C(-1) + 2) == 31); + + B1_CC_ASSERT(b1_math_clz((uint64_t)UINT32_C(-1)) == 32); + B1_CC_ASSERT(b1_math_clz((uint64_t)UINT32_C(-1) + 2) == 31); + + /* make sure it's compile-time constant */ + B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_clz(1U))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_clz((unsigned int)non_constant_expr))); + { + static const int sub = b1_math_clz(1U); + B1_CC_ASSERT(sub == 31); + } + + /* + * Div Round Up: Normal division, but round up to next integer, instead + * of clipping. Also verify that it does not suffer from the integer + * overflow in the prevalant, alternative implementation: + * [(x + y - 1) / y]. + */ +#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y)) + foo = 8; + assert(b1_math_div_round_up(0, 5) == 0); + assert(b1_math_div_round_up(1, 5) == 1); + assert(b1_math_div_round_up(5, 5) == 1); + assert(b1_math_div_round_up(6, 5) == 2); + assert(b1_math_div_round_up(foo++, 1) == 8); + assert(foo == 9); + assert(b1_math_div_round_up(foo++, foo++) >= 0); + assert(foo == 11); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_div_round_up(1, 5))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_div_round_up(1, non_constant_expr))); + + /* alternative calculation is [(x + y - 1) / y], but it may overflow */ + for (i = 0; i <= 0xffff; ++i) { + for (j = 1; j <= 0xff; ++j) + assert(b1_math_div_round_up(i, j) == TEST_ALT_DIV(i, j)); + for (j = 0xff00; j <= 0xffff; ++j) + assert(b1_math_div_round_up(i, j) == TEST_ALT_DIV(i, j)); + } + + /* make sure it doesn't suffer from high overflow */ + assert(UINT32_C(0xfffffffa) % 10 == 0); + assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729)); + assert(b1_math_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729)); + assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */ + + assert(UINT32_C(0xfffffffd) % 10 == 3); + assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729)); + assert(b1_math_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730)); + assert(TEST_ALT_DIV(UINT32_C(0xfffffffd), 10) == 0); +#undef TEST_ALT_DIV + + /* + * Align to multiple of: Test the alignment macro. Check that it does + * not suffer from incorrect integer overflows, neither should it + * exceed the boundaries of the input type. + */ + assert(b1_math_align_to(UINT32_C(0), 1) == 0); + assert(b1_math_align_to(UINT32_C(0), 2) == 0); + assert(b1_math_align_to(UINT32_C(0), 4) == 0); + assert(b1_math_align_to(UINT32_C(0), 8) == 0); + assert(b1_math_align_to(UINT32_C(1), 8) == 8); + + assert(b1_math_align_to(UINT32_C(0xffffffff), 8) == 0); + assert(b1_math_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); + assert(b1_math_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); + + B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_align_to(16, 8))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(non_constant_expr, 8))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(16, non_constant_expr))); + B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(16, non_constant_expr ? 8 : 16))); + B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_align_to(16, 7 + 1))); + assert(b1_math_align_to(15, non_constant_expr ? 8 : 16) == 16); + + for (i = 0; i < 0xffff; ++i) { + assert(b1_math_align(i) == b1_math_align_to(i, (int)sizeof(void*))); + assert(b1_math_align8(i) == b1_math_align_to(i, 8)); + } + + /* + * Align Power2: The b1_math_align_power2() macro aligns passed values to the + * next power of 2. Special cases: 0->0, overflow->0 + * Also make sure it never performs an up-cast on overflow. + */ + assert(b1_math_align_power2(UINT32_C(0)) == 0); + assert(b1_math_align_power2(UINT32_C(0x80000001)) == 0); + assert(b1_math_align_power2(UINT64_C(0)) == 0); + assert(b1_math_align_power2(UINT64_C(0x8000000000000001)) == 0); + + assert(b1_math_align_power2((uint64_t)UINT32_C(0)) == 0); + assert(b1_math_align_power2((uint64_t)UINT32_C(0x80000001)) == UINT64_C(0x100000000)); + + assert(b1_math_align_power2(UINT32_C(1)) == 1); + assert(b1_math_align_power2(UINT32_C(2)) == 2); + assert(b1_math_align_power2(UINT32_C(3)) == 4); + assert(b1_math_align_power2(UINT32_C(4)) == 4); + assert(b1_math_align_power2(UINT32_C(5)) == 8); + assert(b1_math_align_power2(UINT32_C(0x80000000)) == UINT32_C(0x80000000)); +} + +int main(int argc, char **argv) { + test_type(); + test_cc(argc); + test_misc(argc); + test_math(argc); + + return 0; +} diff --git a/src/test-perf.c b/src/test-perf.c new file mode 100644 index 0000000..716be89 --- /dev/null +++ b/src/test-perf.c @@ -0,0 +1,230 @@ +/*** + This file is part of bus1. See COPYING for details. + + bus1 is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + bus1 is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with bus1; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <linux/memfd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/uio.h> +#include "bus1-macro.h" +#include "bus1-sys.h" + +#define TEST_BUFSIZE (4096LL * 4096LL) /* 4096 pages */ + +typedef struct { + uint32_t arg1; + uint32_t arg2; + uint64_t arg3; + uint64_t size; + uint8_t blob[]; +} TestMessage; + +static void test_message_write1(int fd, void *map, const TestMessage *args) { + memcpy(map, args, sizeof(*args) + args->size); +} + +static void test_message_write2(int fd, void *map, const TestMessage *args) { + TestMessage *m = map; + + m->arg1 = args->arg1; + m->arg2 = args->arg2; + m->arg3 = args->arg3; + m->size = args->size; + memcpy(m->blob, args->blob, args->size); +} + +static void test_message_write3(int fd, void *map, const TestMessage *args) { + TestMessage *m; + uint64_t size; + int r; + + size = sizeof(*m) + args->size; + m = alloca(size); + m->arg1 = args->arg1; + m->arg2 = args->arg2; + m->arg3 = args->arg3; + m->size = args->size; + memcpy(m->blob, args->blob, args->size); + + r = pwrite(fd, m, size, 0); + assert(r >= 0 && (uint64_t)r == size); +} + +static void test_message_write4(int fd, void *map, const TestMessage *args) { + struct iovec vec[2]; + TestMessage *m; + int r; + + m = alloca(sizeof(*m)); + m->arg1 = args->arg1; + m->arg2 = args->arg2; + m->arg3 = args->arg3; + m->size = args->size; + + vec[0].iov_base = (void *)m; + vec[0].iov_len = sizeof(*m); + vec[1].iov_base = (void *)args->blob; + vec[1].iov_len = args->size; + + r = pwritev(fd, vec, 2, 0); + assert(r >= 0 && (uint64_t)r == sizeof(*m) + args->size); +} + +static void test_message_write5(int fd, void *map, const TestMessage *args) { + struct iovec vec[5]; + int r; + + vec[0].iov_base = (void *)&args->arg1; + vec[0].iov_len = sizeof(args->arg1); + vec[1].iov_base = (void *)&args->arg2; + vec[1].iov_len = sizeof(args->arg2); + vec[2].iov_base = (void *)&args->arg3; + vec[2].iov_len = sizeof(args->arg3); + vec[3].iov_base = (void *)&args->size; + vec[3].iov_len = sizeof(args->size); + vec[4].iov_base = (void *)args->blob; + vec[4].iov_len = args->size; + + r = pwritev(fd, vec, 5, 0); + assert(r >= 0 && (uint64_t)r == sizeof(TestMessage) + args->size); +} + +static void (* const test_xmitters[]) (int fd, void *map, const TestMessage *args) = { + test_message_write1, + test_message_write2, + test_message_write3, + test_message_write4, + test_message_write5, +}; + +static void test_message_validate(const void *map, const TestMessage *args) { + const TestMessage *m = map; + + assert(args->arg1 == m->arg1); + assert(args->arg2 == m->arg2); + assert(args->arg3 == m->arg3); + assert(args->size == m->size); + assert(!memcmp(args->blob, m->blob, args->size)); +} + +static void test_message_xmit(int fd, void *map, const TestMessage *args, unsigned int xmitter) { + assert(xmitter < B1_CC_ARRAY_SIZE(test_xmitters)); + + test_xmitters[xmitter](fd, map, args); + test_message_validate(map, args); +} + +static void test_xmit(int fd, void *map, unsigned int xmitter, uint64_t times, uint64_t size) { + static struct { + TestMessage m; + uint8_t blob[4096 * 4096]; + } m = { + .m = { + .arg1 = UINT32_C(0xabcdabcd), + .arg2 = UINT32_C(0xffffffff), + .arg3 = UINT64_C(0xff00ff00ff00ff00), + }, + .blob = {}, + }; + uint64_t i; + + assert(size <= sizeof(m.blob)); + + m.m.size = size; + for (i = 0; i < times; ++i) + test_message_xmit(fd, map, &m.m, xmitter); +} + +static void test_run_one(int fd, void *map, unsigned int xmitter, uint64_t times, uint64_t size) { + b1_usec time_start, time_end; + + fprintf(stderr, "Run: times:%" PRIu64 " size:%" PRIu64 "\n", times, size); + + /* do some test runs to initialize caches; don't account them */ + memset(map, 0, TEST_BUFSIZE); + test_xmit(fd, map, xmitter, times / 10, size); + + /* do real tests and measure time */ + time_start = b1_usec_from_clock(CLOCK_THREAD_CPUTIME_ID); + test_xmit(fd, map, xmitter, times, size); + time_end = b1_usec_from_clock(CLOCK_THREAD_CPUTIME_ID); + + /* print result table */ + printf("%" PRIu64 " %d %" PRIu64 "\n", size, xmitter, time_end - time_start); +} + +static void test_run_all(int fd, void *map, unsigned int xmitter, uint64_t times) { + unsigned int size; + + /* + * Run test suite with different blob-sizes. First we start from size 1 + * to 128, doubling on each iteration. Then from 128 to 64k we just add + * 128 each round to get better details. + */ + + for (size = 1; size <= 128; size <<= 1) + test_run_one(fd, map, xmitter, times, size); + for ( ; size <= 4096 << 4; size += 128) + test_run_one(fd, map, xmitter, times, size); +} + +static void test_transaction(unsigned int xmitter) { + int memfd, r; + uint8_t *map; + long i; + + /* create memfd and pre-allocate buffer space */ + memfd = b1_sys_memfd_create("test-file", MFD_CLOEXEC); + assert(memfd >= 0); + + r = fallocate(memfd, 0, 0, TEST_BUFSIZE); + assert(r >= 0); + + map = mmap(NULL, TEST_BUFSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); + assert(map != MAP_FAILED); + + /* access the whole buffer to do some random caching and fault in pages */ + for (i = 0; i < TEST_BUFSIZE; ++i) + assert(map[i] == 0); + + /* run tests; each one 10k times */ + test_run_all(memfd, map, xmitter, 10UL * 1000UL); + + /* cleanup */ + munmap(map, TEST_BUFSIZE); + close(memfd); +} + +int main(int argc, char **argv) { + unsigned int xmitter; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <#xmitter>\n", program_invocation_short_name); + return 77; + } + + xmitter = atoi(argv[1]); + if (xmitter >= B1_CC_ARRAY_SIZE(test_xmitters)) { + fprintf(stderr, "Invalid xmitter (available: %zu)\n", B1_CC_ARRAY_SIZE(test_xmitters)); + return 77; + } + + test_transaction(xmitter); + return 0; +} |