diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 65d04d03..4e766023 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.1.0 +current_version = 2.2.0 commit = False [bumpversion:file:CMakeLists.txt] diff --git a/.cmake/Modules/Capabilities.cmake b/.cmake/Modules/Capabilities.cmake new file mode 100644 index 00000000..6b6395e7 --- /dev/null +++ b/.cmake/Modules/Capabilities.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2015 Franklin "Snaipe" Mathieu. +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the LICENSE file distributed with Criterion. + +include(CheckPrototypeDefinition) + +check_prototype_definition( + strtok_s + "char *strtok_s(char *strToken, const char *strDelimit, char **context)" + NULL + "string.h" + HAVE_STRTOK_S) diff --git a/.cmake/Modules/DebConfig.cmake b/.cmake/Modules/DebConfig.cmake new file mode 100644 index 00000000..3bbc3737 --- /dev/null +++ b/.cmake/Modules/DebConfig.cmake @@ -0,0 +1,42 @@ +# build a Debian package for Launchpad +set(CPACK_DEBIAN_PACKAGE_NAME "criterion") +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +set(CPACK_DEBIAN_PACKAGE_SECTION "libs") +set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/Snaipe/Criterion") +set(CPACK_DEBIAN_BUILD_DEPENDS + debhelper + cmake + gettext + libpcre3-dev +) + +set(CPACK_DEBIAN_PACKAGE_DEPENDS + libpcre3 +) + +set(CPACK_DEBIAN_CMAKE_OPTIONS) +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/description.txt") + +set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY "${CMAKE_SOURCE_DIR}/.cmake/copy-source.sh") + +set(CPACK_DEBIAN_DISTRIBUTION_NAME ubuntu) +set(CPACK_DEBIAN_DISTRIBUTION_RELEASES precise trusty vivid wily xenial) + +set(DPUT_HOST "snaipewastaken-ppa") +set(DPUT_SNAPSHOT_HOST "snaipewastaken-ppa") +set(CPACK_DEBIAN_PACKAGE_DOCS "") +set(CPACK_DEBIAN_PACKAGE_INSTALL + "/usr/lib/*.so" + "/usr/lib/*.so.*" + "/usr/share/locale/*" +) + +set(CPACK_COMPONENTS_ALL "dev") +set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Criterion library development files") +set(CPACK_COMPONENT_DEV_DESCRIPTION "These are the development files.") +set(CPACK_COMPONENT_DEV_SECTION "devel") + +set(CPACK_COMPONENT_DEV_DOCS "") +set(CPACK_COMPONENT_DEV_INSTALL "/usr/include") + +include (DebSourcePPA) diff --git a/.cmake/Modules/DebSourcePPA.cmake b/.cmake/Modules/DebSourcePPA.cmake new file mode 100644 index 00000000..f948ed5b --- /dev/null +++ b/.cmake/Modules/DebSourcePPA.cmake @@ -0,0 +1,347 @@ +## Debian Source Package Generator +# +# Copyright (c) 2010 Daniel Pfeifer +# Many modifications by Rosen Diankov +# +# Creates source debian files and manages library dependencies +# +# Features: +# +# - Automatically generates symbols and run-time dependencies from the build dependencies +# - Custom copy of source directory via CPACK_DEBIAN_PACKAGE_SOURCE_COPY +# - Simultaneous output of multiple debian source packages for each distribution +# - Can specificy distribution-specific dependencies by suffixing DEPENDS with _${DISTRO_NAME}, for example: CPACK_DEBIAN_PACKAGE_DEPENDS_LUCID, CPACK_COMPONENT_MYCOMP0_DEPENDS_LUCID +# +# Usage: +# +# set(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake) +# set(CPACK_DEBIAN_PACKAGE_PRIORITY optional) +# set(CPACK_DEBIAN_PACKAGE_SECTION devel) +# set(CPACK_DEBIAN_CMAKE_OPTIONS "-DMYOPTION=myvalue") +# set(CPACK_DEBIAN_PACKAGE_DEPENDS mycomp0 mycomp1 some_ubuntu_package) +# set(CPACK_DEBIAN_PACKAGE_DEPENDS_UBUNTU_LUCID mycomp0 mycomp1 lucid_specific_package) +# set(CPACK_DEBIAN_PACKAGE_NAME mypackage) +# set(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES unnecessary_file unnecessary_dir/file0) +# set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force) # if using subversion +# set(CPACK_DEBIAN_DISTRIBUTION_NAME ubuntu) +# set(CPACK_DEBIAN_DISTRIBUTION_RELEASES karmic lucid maverick natty) +# set(CPACK_DEBIAN_CHANGELOG " * Extra change log lines") +# set(CPACK_DEBIAN_PACKAGE_SUGGESTS "ipython") +# set(CPACK_COMPONENT_X_RECOMMENDS "recommended-package") +## + +find_program(DEBUILD_EXECUTABLE debuild) +find_program(DPUT_EXECUTABLE dput) + +if(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) + return() +endif(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) + +# DEBIAN/control +# debian policy enforce lower case for package name +# Package: (mandatory) +IF(NOT CPACK_DEBIAN_PACKAGE_NAME) + STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME) +ENDIF(NOT CPACK_DEBIAN_PACKAGE_NAME) + +# Section: (recommended) +IF(NOT CPACK_DEBIAN_PACKAGE_SECTION) + SET(CPACK_DEBIAN_PACKAGE_SECTION "devel") +ENDIF(NOT CPACK_DEBIAN_PACKAGE_SECTION) + +# Priority: (recommended) +IF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) + SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +ENDIF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) + +file(STRINGS ${CPACK_PACKAGE_DESCRIPTION_FILE} DESC_LINES) +foreach(LINE ${DESC_LINES}) + set(DEB_LONG_DESCRIPTION "${DEB_LONG_DESCRIPTION} ${LINE}\n") +endforeach(LINE ${DESC_LINES}) + +file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/Debian") +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Debian") +set(DEBIAN_SOURCE_ORIG_DIR "${CMAKE_BINARY_DIR}/Debian/${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +if( CPACK_DEBIAN_PACKAGE_SOURCE_COPY ) + execute_process(COMMAND ${CPACK_DEBIAN_PACKAGE_SOURCE_COPY} "${CMAKE_SOURCE_DIR}" "${DEBIAN_SOURCE_ORIG_DIR}.orig") +else() + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR} "${DEBIAN_SOURCE_ORIG_DIR}.orig") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.git") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.svn") +endif() + +# remove unnecessary folders +foreach(REMOVE_DIR ${CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES}) + file(REMOVE_RECURSE ${DEBIAN_SOURCE_ORIG_DIR}.orig/${REMOVE_DIR}) +endforeach() + +# create the original source tar +execute_process(COMMAND ${CMAKE_COMMAND} -E tar czf "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}.orig.tar.gz" "${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.orig" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) + +set(DEB_SOURCE_CHANGES) +foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + set(DEBIAN_SOURCE_DIR "${DEBIAN_SOURCE_ORIG_DIR}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") + set(RELEASE_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") + string(TOUPPER ${RELEASE} RELEASE_UPPER) + string(TOUPPER ${CPACK_DEBIAN_DISTRIBUTION_NAME} DISTRIBUTION_NAME_UPPER) + file(MAKE_DIRECTORY ${DEBIAN_SOURCE_DIR}/debian) + ############################################################################## + # debian/control + set(DEBIAN_CONTROL ${DEBIAN_SOURCE_DIR}/debian/control) + file(WRITE ${DEBIAN_CONTROL} + "Source: ${CPACK_DEBIAN_PACKAGE_NAME}\n" + "Section: ${CPACK_DEBIAN_PACKAGE_SECTION}\n" + "Priority: ${CPACK_DEBIAN_PACKAGE_PRIORITY}\n" + "DM-Upload-Allowed: yes\n" + "Maintainer: ${CPACK_PACKAGE_CONTACT}\n" + "Build-Depends: " + ) + + if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) + endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Standards-Version: 3.8.4\n" + "Homepage: ${CPACK_PACKAGE_VENDOR}\n" + "\n" + "Package: ${CPACK_DEBIAN_PACKAGE_NAME}\n" + "Architecture: any\n" + "Depends: " + ) + + if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) + endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\nRecommends: ") + if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) + endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\nSuggests: ") + if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) + endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n" + "${DEB_LONG_DESCRIPTION}" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + string(TOUPPER ${COMPONENT} UPPER_COMPONENT) + set(DEPENDS "\${shlibs:Depends}") + if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + set(RECOMMENDS) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + set(SUGGESTS) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Package: ${CPACK_DEBIAN_PACKAGE_NAME}-${COMPONENT}\n" + "Architecture: any\n" + "Depends: ${DEPENDS}\n" + "Recommends: ${RECOMMENDS}\n" + "Suggests: ${SUGGESTS}\n" + "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_COMPONENT_${UPPER_COMPONENT}_DISPLAY_NAME}\n" + "${DEB_LONG_DESCRIPTION}" + " .\n" + " ${CPACK_COMPONENT_${UPPER_COMPONENT}_DESCRIPTION}\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + ############################################################################## + # debian/copyright + set(DEBIAN_COPYRIGHT ${DEBIAN_SOURCE_DIR}/debian/copyright) + execute_process(COMMAND ${CMAKE_COMMAND} -E + copy ${CPACK_RESOURCE_FILE_LICENSE} ${DEBIAN_COPYRIGHT} + ) + + ############################################################################## + # debian/rules + set(DEBIAN_RULES ${DEBIAN_SOURCE_DIR}/debian/rules) + file(WRITE ${DEBIAN_RULES} + "#!/usr/bin/make -f\n" + "\n" + "BUILDDIR = build_dir\n" + "\n" + "build:\n" + " mkdir $(BUILDDIR)\n" + " cd $(BUILDDIR); cmake -DCMAKE_BUILD_TYPE=Release ${CPACK_DEBIAN_CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=/usr ..\n" + " $(MAKE) -C $(BUILDDIR) preinstall\n" + " touch build\n" + "\n" + "binary: binary-indep binary-arch\n" + "\n" + "binary-indep: build\n" + "\n" + "binary-arch: build\n" + " cd $(BUILDDIR); cmake -DCOMPONENT=Unspecified -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr -P cmake_install.cmake\n" + " mkdir -p debian/tmp/DEBIAN\n" + " dpkg-gensymbols -p${CPACK_DEBIAN_PACKAGE_NAME}\n" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + set(PATH debian/${COMPONENT}) + file(APPEND ${DEBIAN_RULES} + " cd $(BUILDDIR); cmake -DCOMPONENT=${COMPONENT} -DCMAKE_INSTALL_PREFIX=../${PATH}/usr -P cmake_install.cmake\n" + " mkdir -p ${PATH}/DEBIAN\n" + " dpkg-gensymbols -p${CPACK_DEBIAN_PACKAGE_NAME}-${COMPONENT} -P${PATH}\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + file(APPEND ${DEBIAN_RULES} + " dh_shlibdeps\n" + " dh_strip\n" # for reducing size + " dpkg-gencontrol -p${CPACK_DEBIAN_PACKAGE_NAME}\n" + " dpkg --build debian/tmp ..\n" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + set(PATH debian/${COMPONENT}) + file(APPEND ${DEBIAN_RULES} + " dpkg-gencontrol -p${CPACK_DEBIAN_PACKAGE_NAME}-${COMPONENT} -P${PATH} -Tdebian/${COMPONENT}.substvars\n" + " dpkg --build ${PATH} ..\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + file(APPEND ${DEBIAN_RULES} + "\n" + "clean:\n" + " rm -f build\n" + " rm -rf $(BUILDDIR)\n" + "\n" + ".PHONY: binary binary-arch binary-indep clean\n" + ) + + execute_process(COMMAND chmod +x ${DEBIAN_RULES}) + + ############################################################################## + # debian/compat + file(WRITE ${DEBIAN_SOURCE_DIR}/debian/compat "7") + + ############################################################################## + # debian/source/format + file(WRITE ${DEBIAN_SOURCE_DIR}/debian/source/format "3.0 (quilt)") + + ############################################################################## + # debian/changelog + set(DEBIAN_CHANGELOG ${DEBIAN_SOURCE_DIR}/debian/changelog) + execute_process(COMMAND date -R OUTPUT_VARIABLE DATE_TIME) + file(WRITE ${DEBIAN_CHANGELOG} + "${CPACK_DEBIAN_PACKAGE_NAME} (${RELEASE_PACKAGE_VERSION}) ${RELEASE}; urgency=medium\n\n" + " * Package built with CMake\n\n" + "${CPACK_DEBIAN_CHANGELOG}" + " -- ${CPACK_PACKAGE_CONTACT} ${DATE_TIME}" + ) + + ############################################################################## + # debuild -S + if( DEB_SOURCE_CHANGES ) + set(DEBUILD_OPTIONS "-sd") + else() + set(DEBUILD_OPTIONS "-sa") + endif() + set(SOURCE_CHANGES_FILE "${CPACK_DEBIAN_PACKAGE_NAME}_${RELEASE_PACKAGE_VERSION}_source.changes") + set(DEB_SOURCE_CHANGES ${DEB_SOURCE_CHANGES} "${SOURCE_CHANGES_FILE}") + add_custom_command(OUTPUT "${SOURCE_CHANGES_FILE}" COMMAND ${DEBUILD_EXECUTABLE} -S ${DEBUILD_OPTIONS} WORKING_DIRECTORY ${DEBIAN_SOURCE_DIR}) +endforeach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + +############################################################################## +# dput ppa:your-lp-id/ppa +add_custom_target(dput ${DPUT_EXECUTABLE} ${DPUT_HOST} ${DEB_SOURCE_CHANGES} DEPENDS ${DEB_SOURCE_CHANGES} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) diff --git a/.cmake/Modules/GettextTranslate.cmake b/.cmake/Modules/GettextTranslate.cmake index 553b13d1..4c42ef47 100644 --- a/.cmake/Modules/GettextTranslate.cmake +++ b/.cmake/Modules/GettextTranslate.cmake @@ -252,13 +252,29 @@ macro(GettextTranslate) else() - add_custom_target(${PO_TARGET} - COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --lang=${lang} - ${PO_FILE_NAME} ${TEMPLATE_FILE_ABS} - -o ${PO_FILE_NAME}.new - COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME} - DEPENDS ${TEMPLATE_FILE_ABS} + execute_process( + COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --version + OUTPUT_VARIABLE MSGMERGE_VERSION_MSG ) + string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" MSGMERGE_VERSION "${MSGMERGE_VERSION_MSG}") + + if ("${MSGMERGE_VERSION}" VERSION_GREATER "0.17") + add_custom_target(${PO_TARGET} + COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --lang=${lang} + ${PO_FILE_NAME} ${TEMPLATE_FILE_ABS} + -o ${PO_FILE_NAME}.new + COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME} + DEPENDS ${TEMPLATE_FILE_ABS} + ) + else () + add_custom_target(${PO_TARGET} + COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} + ${PO_FILE_NAME} ${TEMPLATE_FILE_ABS} + -o ${PO_FILE_NAME}.new + COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME} + DEPENDS ${TEMPLATE_FILE_ABS} + ) + endif () endif() @@ -271,7 +287,7 @@ macro(GettextTranslate) add_dependencies(${PO_TARGET} ${MAKEVAR_DOMAIN}.pot-update) install(FILES ${GMO_FILE_NAME} DESTINATION - ${LOCALEDIR}/${lang}/LC_MESSAGES + ${LOCALEDIR_REL}/${lang}/LC_MESSAGES RENAME ${MAKEVAR_DOMAIN}.mo ) diff --git a/.cmake/Modules/PackageConfig.cmake b/.cmake/Modules/PackageConfig.cmake new file mode 100644 index 00000000..632d9b8a --- /dev/null +++ b/.cmake/Modules/PackageConfig.cmake @@ -0,0 +1,79 @@ + +function(extract_version version major minor patch extra) + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" version_valid ${version}) + if(version_valid) + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" "\\1;\\2;\\3;\\4" VERSION_MATCHES ${version}) + list(GET VERSION_MATCHES 0 version_major) + set(${major} ${version_major} PARENT_SCOPE) + list(GET VERSION_MATCHES 1 version_minor) + set(${minor} ${version_minor} PARENT_SCOPE) + list(GET VERSION_MATCHES 2 version_patch) + set(${patch} ${version_patch} PARENT_SCOPE) + list(GET VERSION_MATCHES 3 version_extra) + set(${extra} ${version_extra} PARENT_SCOPE) + else(version_valid) + message(AUTHOR_WARNING "Bad version ${version}; falling back to 0 (have you made an initial release?)") + set(${major} "0" PARENT_SCOPE) + set(${minor} "" PARENT_SCOPE) + set(${patch} "" PARENT_SCOPE) + set(${extra} "" PARENT_SCOPE) + endif(version_valid) +endfunction(extract_version) + +if (WIN32) + set(CPACK_GENERATOR "ZIP") + set(CPACK_SOURCE_GENERATOR "ZIP") +else () + set(CPACK_GENERATOR "TGZ") + set(CPACK_SOURCE_GENERATOR "TGZ") +endif () + +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A KISS, modern unit testing framework for C and C++.") +set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-binary-${PROJECT_VERSION}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}") +set(CPACK_PACKAGE_VENDOR "Franklin \"Snaipe\" Mathieu") +set(CPACK_PACKAGE_CONTACT "Franklin \"Snaipe\" Mathieu ") +set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") + +if (WIN32) + # add snapshot specific versioning information + if (CPACK_DEBIAN_PACKAGE_TYPE STREQUAL "snapshot") + execute_process(COMMAND date +%Y%m%d%0k%0M%0S%z OUTPUT_VARIABLE SNAPSHOT_DATE_TIME) + set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}-snapshot-${SNAPSHOT_DATE_TIME}") + STRING(REPLACE "\n" "" CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) + endif () +endif () + +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/debian.copyright") +extract_version(${PROJECT_VERSION} + CPACK_PACKAGE_VERSION_MAJOR + CPACK_PACKAGE_VERSION_MINOR + CPACK_PACKAGE_VERSION_PATCH + VERSION_EXTRA +) + +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) + +file(GLOB TRASH_FILES "${CMAKE_SOURCE_DIR}/*") +set(KEEP_FILES + "${CMAKE_SOURCE_DIR}/.cmake" + "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/doc" + "${CMAKE_SOURCE_DIR}/dev" + "${CMAKE_SOURCE_DIR}/po" + "${CMAKE_SOURCE_DIR}/dependencies" + "${CMAKE_SOURCE_DIR}/CMakeLists.txt" + "${CMAKE_SOURCE_DIR}/README.md" + "${CMAKE_SOURCE_DIR}/CONTRIBUTING.md" + "${CMAKE_SOURCE_DIR}/LICENSE" + "${CMAKE_SOURCE_DIR}/ChangeLog" + "${CMAKE_SOURCE_DIR}/description.txt" +) +list(REMOVE_ITEM TRASH_FILES ${KEEP_FILES}) +# Escape any '.' characters +string(REPLACE "." "\\\\." TRASH_FILES "${TRASH_FILES}") +set(CPACK_SOURCE_IGNORE_FILES "${TRASH_FILES}") + +include(CPack) diff --git a/.cmake/copy-source.sh b/.cmake/copy-source.sh new file mode 100755 index 00000000..fc562c62 --- /dev/null +++ b/.cmake/copy-source.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +CURDIR=$(dirname $0) +SOURCE_DIR=$1; shift +DEST_DIR=$1; shift + +( + cd "$SOURCE_DIR" + mkdir -p "$DEST_DIR" + "$CURDIR/git-archive-all.sh" --format tar -- - | tar -x -C "$DEST_DIR" +) diff --git a/.cmake/git-archive-all.sh b/.cmake/git-archive-all.sh new file mode 100755 index 00000000..9a17eece --- /dev/null +++ b/.cmake/git-archive-all.sh @@ -0,0 +1,302 @@ +#!/bin/bash - +# +# File: git-archive-all.sh +# +# Description: A utility script that builds an archive file(s) of all +# git repositories and submodules in the current path. +# Useful for creating a single tarfile of a git super- +# project that contains other submodules. +# +# Examples: Use git-archive-all.sh to create archive distributions +# from git repositories. To use, simply do: +# +# cd $GIT_DIR; git-archive-all.sh +# +# where $GIT_DIR is the root of your git superproject. +# +# License: GPL3+ +# +############################################################################### +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +############################################################################### + +# DEBUGGING +set -e +set -C # noclobber + +# TRAP SIGNALS +trap 'cleanup' QUIT EXIT + +# For security reasons, explicitly set the internal field separator +# to newline, space, tab +OLD_IFS=$IFS +IFS=' + ' + +function cleanup () { + rm -f $TMPFILE + rm -f $TOARCHIVE + IFS="$OLD_IFS" +} + +function usage () { + echo "Usage is as follows:" + echo + echo "$PROGRAM <--version>" + echo " Prints the program version number on a line by itself and exits." + echo + echo "$PROGRAM <--usage|--help|-?>" + echo " Prints this usage output and exits." + echo + echo "$PROGRAM [--format ] [--prefix ] [--verbose|-v] [--separate|-s]" + echo " [--tree-ish|-t ] [output_file]" + echo " Creates an archive for the entire git superproject, and its submodules" + echo " using the passed parameters, described below." + echo + echo " If '--format' is specified, the archive is created with the named" + echo " git archiver backend. Obviously, this must be a backend that git archive" + echo " understands. The format defaults to 'tar' if not specified." + echo + echo " If '--prefix' is specified, the archive's superproject and all submodules" + echo " are created with the prefix named. The default is to not use one." + echo + echo " If '--separate' or '-s' is specified, individual archives will be created" + echo " for each of the superproject itself and its submodules. The default is to" + echo " concatenate individual archives into one larger archive." + echo + echo " If '--tree-ish' is specified, the archive will be created based on whatever" + echo " you define the tree-ish to be. Branch names, commit hash, etc. are acceptable." + echo " Defaults to HEAD if not specified. See git archive's documentation for more" + echo " information on what a tree-ish is." + echo + echo " If 'output_file' is specified, the resulting archive is created as the" + echo " file named. This parameter is essentially a path that must be writeable." + echo " When combined with '--separate' ('-s') this path must refer to a directory." + echo " Without this parameter or when combined with '--separate' the resulting" + echo " archive(s) are named with a dot-separated path of the archived directory and" + echo " a file extension equal to their format (e.g., 'superdir.submodule1dir.tar')." + echo + echo " The special value '-' (single dash) is treated as STDOUT and, when used, the" + echo " --separate option is ignored. Use a double-dash to separate the outfile from" + echo " the value of previous options. For example, to write a .zip file to STDOUT:" + echo + echo " ./$PROGRAM --format zip -- -" + echo + echo " If '--verbose' or '-v' is specified, progress will be printed." +} + +function version () { + echo "$PROGRAM version $VERSION" +} + +# Internal variables and initializations. +readonly PROGRAM=`basename "$0"` +readonly VERSION=0.3 + +SEPARATE=0 +VERBOSE=0 + +TARCMD=`command -v gnutar || command -v tar` +FORMAT=tar +PREFIX= +TREEISH=HEAD + +# RETURN VALUES/EXIT STATUS CODES +readonly E_BAD_OPTION=254 +readonly E_UNKNOWN=255 + +# Process command-line arguments. +while test $# -gt 0; do + if [ x"$1" == x"--" ]; then + # detect argument termination + shift + break + fi + case $1 in + --format ) + shift + FORMAT="$1" + shift + ;; + + --prefix ) + shift + PREFIX="$1" + shift + ;; + + --separate | -s ) + shift + SEPARATE=1 + ;; + + --tree-ish | -t ) + shift + TREEISH="$1" + shift + ;; + + --version ) + version + exit + ;; + + --verbose | -v ) + shift + VERBOSE=1 + ;; + + -? | --usage | --help ) + usage + exit + ;; + + -* ) + echo "Unrecognized option: $1" >&2 + usage + exit $E_BAD_OPTION + ;; + + * ) + break + ;; + esac +done + +OLD_PWD="`pwd`" +TMPDIR=${TMPDIR:-/tmp} +TMPFILE=`mktemp "$TMPDIR/$PROGRAM.XXXXXX"` # Create a place to store our work's progress +TOARCHIVE=`mktemp "$TMPDIR/$PROGRAM.toarchive.XXXXXX"` +OUT_FILE=$OLD_PWD # assume "this directory" without a name change by default + +if [ ! -z "$1" ]; then + OUT_FILE="$1" + if [ "-" == $OUT_FILE ]; then + SEPARATE=0 + fi + shift +fi + +# Validate parameters; error early, error often. +if [ "-" == $OUT_FILE -o $SEPARATE -ne 1 ] && [ "$FORMAT" == "tar" -a `$TARCMD --help | grep -q -- "--concatenate"; echo $?` -ne 0 ]; then + echo "Your 'tar' does not support the '--concatenate' option, which we need" + echo "to produce a single tarfile. Either install a compatible tar (such as" + echo "gnutar), or invoke $PROGRAM with the '--separate' option." + exit +elif [ $SEPARATE -eq 1 -a ! -d $OUT_FILE ]; then + echo "When creating multiple archives, your destination must be a directory." + echo "If it's not, you risk being surprised when your files are overwritten." + exit +elif [ `git config -l | grep -q '^core\.bare=false'; echo $?` -ne 0 ]; then + echo "$PROGRAM must be run from a git working copy (i.e., not a bare repository)." + exit +fi + +# Create the superproject's git-archive +if [ $VERBOSE -eq 1 ]; then + echo -n "creating superproject archive..." +fi +git archive --format=$FORMAT --prefix="$PREFIX" $TREEISH > $TMPDIR/$(basename "$(pwd)").$FORMAT +if [ $VERBOSE -eq 1 ]; then + echo "done" +fi +echo $TMPDIR/$(basename "$(pwd)").$FORMAT >| $TMPFILE # clobber on purpose +superfile=`head -n 1 $TMPFILE` + +if [ $VERBOSE -eq 1 ]; then + echo -n "looking for subprojects..." +fi +# find all '.git' dirs, these show us the remaining to-be-archived dirs +# we only want directories that are below the current directory +find . -mindepth 2 -name '.git' -type d -print | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE +# as of version 1.7.8, git places the submodule .git directories under the superprojects .git dir +# the submodules get a .git file that points to their .git dir. we need to find all of these too +find . -mindepth 2 -name '.git' -type f -print | xargs grep -l "gitdir" | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE +if [ $VERBOSE -eq 1 ]; then + echo "done" + echo " found:" + cat $TOARCHIVE | while read arch + do + echo " $arch" + done +fi + +if [ $VERBOSE -eq 1 ]; then + echo -n "archiving submodules..." +fi +while read path; do + TREEISH=$(git submodule | grep "^ .*${path%/} " | cut -d ' ' -f 2) # git submodule does not list trailing slashes in $path + cd "$path" + git archive --format=$FORMAT --prefix="${PREFIX}$path" ${TREEISH:-HEAD} > "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT + if [ $FORMAT == 'zip' ]; then + # delete the empty directory entry; zipped submodules won't unzip if we don't do this + zip -d "$(tail -n 1 $TMPFILE)" "${PREFIX}${path%/}" >/dev/null # remove trailing '/' + fi + echo "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT >> $TMPFILE + cd "$OLD_PWD" +done < $TOARCHIVE +if [ $VERBOSE -eq 1 ]; then + echo "done" +fi + +if [ $VERBOSE -eq 1 ]; then + echo -n "concatenating archives into single archive..." +fi +# Concatenate archives into a super-archive. +if [ $SEPARATE -eq 0 -o "-" == $OUT_FILE ]; then + if [ $FORMAT == 'tar.gz' ]; then + gunzip $superfile + superfile=${superfile:0: -3} # Remove '.gz' + sed -e '1d' $TMPFILE | while read file; do + gunzip $file + file=${file:0: -3} + $TARCMD --concatenate -f "$superfile" "$file" && rm -f "$file" + done + gzip $superfile + superfile=$superfile.gz + elif [ $FORMAT == 'tar' ]; then + sed -e '1d' $TMPFILE | while read file; do + $TARCMD --concatenate -f "$superfile" "$file" && rm -f "$file" + done + elif [ $FORMAT == 'zip' ]; then + sed -e '1d' $TMPFILE | while read file; do + # zip incorrectly stores the full path, so cd and then grow + cd `dirname "$file"` + zip -g "$superfile" `basename "$file"` && rm -f "$file" + done + cd "$OLD_PWD" + fi + + echo "$superfile" >| $TMPFILE # clobber on purpose +fi +if [ $VERBOSE -eq 1 ]; then + echo "done" +fi + +if [ $VERBOSE -eq 1 ]; then + echo -n "moving archive to $OUT_FILE..." +fi +while read file; do + if [ "-" == $OUT_FILE ]; then + cat "$file" && rm -f "$file" + else + mv "$file" "$OUT_FILE" + fi +done < $TMPFILE +if [ $VERBOSE -eq 1 ]; then + echo "done" +fi diff --git a/.gitignore b/.gitignore index e28ebbfa..dc27c604 100644 --- a/.gitignore +++ b/.gitignore @@ -10,13 +10,16 @@ !doc/* !*.c +!*.m !*.cc !*.h +!*.hxx !*.rst !*.po !*.in +!.cmake/Modules/*.cmake !samples/tests/*.sh -!samples/*.expected +!samples/**/*.expected !LICENSE !HEADER diff --git a/.gitmodules b/.gitmodules index 7c24be15..7ff4b273 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "dependencies/wingetopt"] path = dependencies/wingetopt url = https://github.com/alex85k/wingetopt.git +[submodule "dependencies/klib"] + path = dependencies/klib + url = https://github.com/attractivechaos/klib.git diff --git a/.travis.yml b/.travis.yml index d9a7c9aa..57fa7592 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,49 +1,130 @@ language: c -os: -- linux -- osx - -compiler: -- gcc-4.9 sudo: false -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - gcc-4.9 - - g++-4.9 -env: - global: - GCOV: gcov-4.9 - CXX: g++-4.9 - matrix: - - CONFIGURATION=Debug COVERAGE=ON - - CONFIGURATION=Release COVERAGE=OFF - - CONFIGURATION=RelWithDebInfo COVERAGE=OFF +_anchors: + - &gcc49-packages + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.9 + - g++-4.9 + - gobjc-4.9 + - gnustep-devel + +matrix: + include: + # Linux Debug, GCC 4.9 + - compiler: gcc-4.9 + addons: *gcc49-packages + env: + CONFIGURATION: Debug + GCOV: gcov-4.9 + CMAKE_OPTS: -DLANG_OBJC=ON + COVERAGE: "ON" + # Linux Release, GCC 4.9 + - compiler: gcc-4.9 + addons: *gcc49-packages + env: + CONFIGURATION: Release + CMAKE_OPTS: -DLANG_OBJC=ON + # Linux RelWithDebInfo, GCC 4.9 + - compiler: gcc-4.9 + addons: *gcc49-packages + env: + CONFIGURATION: RelWithDebInfo + CMAKE_OPTS: -DLANG_OBJC=ON + DEPLOY: true + # Linux Debug, GCC 4.6 + - compiler: gcc + env: CONFIGURATION=Debug TESTS=OFF + # Linux Release, GCC 4.6 + - compiler: gcc + env: CONFIGURATION=Release TESTS=OFF + # Linux RelWithDebInfo, GCC 4.6 + - compiler: gcc + env: CONFIGURATION=RelWithDebInfo TESTS=OFF + # OSX Debug, GCC 4.9 + - os: osx + compiler: gcc-4.9 + env: + CONFIGURATION: Debug + GCOV: gcov-4.9 + COVERAGE: "ON" + # OSX Release, GCC 4.9 + - os: osx + compiler: gcc-4.9 + env: CONFIGURATION=Release + # OSX RelWithDebInfo, GCC 4.9 + - os: osx + compiler: gcc-4.9 + env: CONFIGURATION=RelWithDebInfo + # OSX Debug, Clang + - os: osx + compiler: clang + env: + CONFIGURATION: Debug + CMAKE_OPTS: -DLANG_OBJC=ON + # OSX Release, Clang + - os: osx + compiler: clang + env: + CONFIGURATION: Release + CMAKE_OPTS: -DLANG_OBJC=ON + # OSX RelWithDebInfo, Clang + - os: osx + compiler: clang + env: + CONFIGURATION: RelWithDebInfo + CMAKE_OPTS: -DLANG_OBJC=ON + DEPLOY: true + + allow_failures: + - compiler: gcc + +before_install: + - | + if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$CC" = "clang" ]; then + brew update + brew unlink cmake + brew install llvm cmake + fi + - export CXX=${CC/gcc/g++}; export CXX=${CXX/clang/clang++} + - $CC --version + - $CXX --version script: - - mkdir -p build - - cd build + - mkdir -p build && cd $_ - > cmake -Wno-dev - -DCOVERALLS=${COVERAGE} + -DCTESTS=${TESTS:-ON} + -DCOVERALLS=${COVERAGE:-OFF} -DCMAKE_BUILD_TYPE=${CONFIGURATION} -DCMAKE_INSTALL_PREFIX=criterion-${TRAVIS_TAG} + ${CMAKE_OPTS} .. - - make - - make criterion_tests - - make test + - | + if [ "${TESTS:-ON}" = "ON" ]; then + TERM=dumb cmake --build . --target criterion_tests -- -j4 + ctest -j4 + else + TERM=dumb cmake --build . -- -j4 + fi after_success: - - make gcov - - bash <(curl -s https://codecov.io/bash) + - | + if [ "$COVERAGE" = "ON" ]; then + make gcov + bash <(curl -s https://codecov.io/bash) + fi after_failure: - - cat Testing/Temporary/LastTest.log samples/*.{out,err} ../samples/tests/*.{out,err} + - | + if [ "$TESTS" = "ON" ]; then + cat Testing/Temporary/LastTest.log samples/*.{out,err} ../samples/tests/*.{out,err} + fi before_deploy: - make install @@ -58,4 +139,4 @@ deploy: on: repo: Snaipe/Criterion tags: true - condition: $CONFIGURATION = RelWithDebInfo + condition: $DEPLOY = true diff --git a/CMakeLists.txt b/CMakeLists.txt index 73c04551..258c9701 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,41 @@ cmake_minimum_required(VERSION 2.8) -project(Criterion C CXX) +project(Criterion C) set(MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.cmake/Modules") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${MODULE_DIR}) set(LIBCSPTR_DISABLE_TESTS ON) set(LIBCSPTR_DISABLE_COVERALLS ON) +# Content options + +option(THEORIES "Activate the support for theories" ON) + +# Initialization + include(Submodules) +include(Capabilities) if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) endif () add_subdirectory(dependencies/libcsptr/ EXCLUDE_FROM_ALL) -add_subdirectory(dependencies/dyncall/ EXCLUDE_FROM_ALL) + +if (THEORIES) + add_subdirectory(dependencies/dyncall/ EXCLUDE_FROM_ALL) + include_directories(dependencies/dyncall/dyncall/) +endif () + +include_directories(SYSTEM + /usr/local/include + /usr/include/GNUstep +) include_directories( - /usr/local/include/ dependencies/libcsptr/include/ - dependencies/dyncall/dyncall/ + dependencies/valgrind/include/ + dependencies/klib/ ) if (MSVC) @@ -27,17 +43,45 @@ if (MSVC) include_directories(dependencies/wingetopt/src/) endif () +# Check for C++11 + +option(LANG_CXX "Turn on C++ support" ON) +if (LANG_CXX) + enable_language(CXX) +endif () + +if (NOT MSVC AND CMAKE_CXX_COMPILER_WORKS) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + + if(COMPILER_SUPPORTS_CXX11) + set(CXX11_FLAG "-std=c++11") + elseif(COMPILER_SUPPORTS_CXX0X) + set(CXX11_FLAG "-std=c++0x") + else() + message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} has no C++11 support.") + endif() +endif() + # Project setup & environment variables -set(PROJECT_VERSION "2.1.0") -set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) +set(PROJECT_VERSION "2.2.0") +set(LOCALEDIR_REL "share/locale") +set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/${LOCALEDIR_REL}") set(GettextTranslate_ALL 1) set(GettextTranslate_GMO_BINARY 1) add_definitions(-DCRITERION_BUILDING_DLL=1) +set(CMAKE_C_FLAGS_DEFAULT "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS_DEFAULT "${CMAKE_CXX_FLAGS}") + if (NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -g -std=gnu99") + if (CMAKE_CXX_COMPILER_WORKS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -g ${CXX11_FLAG}") + endif () endif () if (MSVC) @@ -48,10 +92,20 @@ if (WIN32 AND NOT MSVC) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-no-undefined") endif() +# Compilation options + +option(MINGW_DEFINE_OFF_T "Define off_t and off64_t ourselves before including io.h" OFF) + # Setup coveralls option(COVERALLS "Turn on coveralls support" OFF) option(COVERALLS_UPLOAD "Upload the generated coveralls json" ON) +option(DEV_BUILD "Compile in developer mode" OFF) +option(CTESTS "Turn on the samples and test" ${DEV_BUILD}) + +if (DEV_BUILD) + set(ENABLE_VALGRIND_ERRORS 1) +endif () if (COVERALLS) include(Coveralls) @@ -60,12 +114,16 @@ endif() # Find dependencies -find_package(Gettext) -find_package(Libintl) -if (GETTEXT_FOUND AND LIBINTL_LIB_FOUND) - include(GettextTranslate) - add_subdirectory(po) - set(ENABLE_NLS 1) +option(I18N "Turn on internationalization" ON) + +if (I18N) + find_package(Gettext) + find_package(Libintl) + if (GETTEXT_FOUND AND LIBINTL_LIB_FOUND) + include(GettextTranslate) + add_subdirectory(po) + set(ENABLE_NLS 1) + endif () endif () include(CheckLibraryExists) @@ -82,15 +140,19 @@ set(SOURCE_FILES src/core/report.h src/core/runner.c src/core/runner.h + src/core/runner_coroutine.c + src/core/runner_coroutine.h + src/core/coroutine.h src/core/worker.c src/core/worker.h src/core/stats.c src/core/stats.h src/core/ordered-set.c - src/core/theories.c + src/core/test.c src/compat/internal.h src/compat/pipe.c src/compat/pipe.h + src/compat/pipe-internal.h src/compat/section.c src/compat/section.h src/compat/process.c @@ -103,21 +165,35 @@ set(SOURCE_FILES src/compat/posix.h src/compat/alloc.c src/compat/alloc.h + src/compat/processor.c + src/compat/processor.h src/io/redirect.c src/io/event.c src/io/event.h src/io/asprintf.c src/io/file.c + src/io/output.c + src/io/output.h + src/io/tap.c + src/io/xml.c + src/io/json.c src/log/logging.c - src/log/tap.c src/log/normal.c src/string/i18n.c src/string/i18n.h src/entry/options.c - src/entry/main.c + src/entry/params.c src/entry/entry.c + src/common.h + src/config.h ) +if (THEORIES) + set (SOURCE_FILES ${SOURCE_FILES} + src/core/theories.c + ) +endif () + if (PCRE_FOUND) set (SOURCE_FILES ${SOURCE_FILES} src/string/extmatch.c @@ -129,22 +205,39 @@ endif () set(INTERFACE_FILES include/criterion/assert.h include/criterion/abort.h - include/criterion/common.h include/criterion/criterion.h include/criterion/event.h include/criterion/hooks.h include/criterion/logging.h include/criterion/types.h include/criterion/options.h - include/criterion/ordered-set.h include/criterion/stats.h - include/criterion/theories.h - include/criterion/asprintf-compat.h - include/criterion/designated-initializer-compat.h - include/criterion/preprocess.h include/criterion/alloc.h + include/criterion/parameterized.h + include/criterion/redirect.h + include/criterion/output.h + include/criterion/internal/assert.h + include/criterion/internal/test.h + include/criterion/internal/common.h + include/criterion/internal/ordered-set.h + include/criterion/internal/asprintf-compat.h + include/criterion/internal/designated-initializer-compat.h + include/criterion/internal/preprocess.h + include/criterion/internal/parameterized.h + include/criterion/internal/stdio_filebuf.hxx + include/criterion/internal/stream.hxx + include/criterion/internal/hooks.h + include/criterion/internal/redirect.h + include/criterion/internal/stdio_filebuf.hxx ) +if (THEORIES) + set(INTERFACE_FILES ${INTERFACE_FILES} + include/criterion/theories.h + include/criterion/internal/theories.h + ) +endif() + # Generate the configure file configure_file( @@ -154,7 +247,11 @@ configure_file( include_directories(include src) add_library(criterion SHARED ${SOURCE_FILES} ${INTERFACE_FILES}) -target_link_libraries(criterion csptr dyncall_s) +target_link_libraries(criterion csptr) + +if (THEORIES) + target_link_libraries(criterion dyncall_s) +endif () if (MSVC) target_link_libraries(criterion wingetopt) @@ -170,17 +267,22 @@ endif() if (LIBINTL_LIB_FOUND) target_link_libraries(criterion ${LIBINTL_LIBRARIES}) + include_directories(${LIBINTL_INCLUDE_DIR}) endif() if (COVERALLS) coveralls_setup("${SOURCE_FILES}" ${COVERALLS_UPLOAD}) endif() -install(FILES ${INTERFACE_FILES} DESTINATION include/criterion) +foreach (F ${INTERFACE_FILES}) + get_filename_component(DEST "${F}" PATH) + install(FILES "${F}" DESTINATION "${DEST}" COMPONENT dev) +endforeach () + install(TARGETS criterion RUNTIME DESTINATION bin LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib + ARCHIVE DESTINATION lib COMPONENT dev ) add_custom_target(criterion_tests) @@ -192,6 +294,19 @@ add_custom_target(gcov -P "${CMAKE_MODULE_PATH}/Gcov.cmake" ) +if (CTESTS) enable_testing() add_subdirectory(samples) add_subdirectory(test) +endif () + +# Add toolchain patch number for incremental deb builds +set(PROJECT_VERSION "${PROJECT_VERSION}-6") + +include (PackageConfig) + +option(UPLOAD_DEB "Upload package to launchpad" OFF) + +if (UNIX AND UPLOAD_DEB) + include (DebConfig) +endif () diff --git a/ChangeLog b/ChangeLog index da131a9f..fd731fdd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2015-12-08 Franklin "Snaipe" Mathieu + + * criterion: version 2.2.0 + * Breaking: Renamed all unprefixed internal macros and functions that were + exposed in the API, and moved them to criterion/internal. + This shouldn't break your code if you did not use these in the first + place. + * Change: Added language-specific wrapping logic to decouple the language + the tests are written in from the test runner. + * Change: Rewrote the reporting logic to allow multiple test reports to be + written using any format. + * Addition: Added parallel jobs for the test runner. + * Addition: Added C++ allocator for STL collections based on + cr_malloc/cr_free. + * Addition: Added criterion::parameters in C++ for simpler parameter list + generation. + * Addition: Added saner defaults when the tests detect they run under + valgrind. + * Addition: Added basic Objective-C language support. + * Addition: Added JUnit XML reporting. + * Addition: Added JSON reporting. + * Addition: Added dynamic reporter registration. + * Addition: Added back support for GCC 4.6 when compiling C tests. + * Addition: Added single test execution mode. + * Removal: Removed all deprecated 1.x unprefixed assertion macros. + * Fix: Fixed some memory corruption happening on rare occasions on assert + messages. + * Fix: Fixed deadlocks happening at random when a large quantity of assert + is present. + * Fix: Fixed the library not compiling with the intel compiler collection. + * Deprecation: All cr_assume_strings_* macros are deprecated in favor of + cr_assume_str_*. + * Deprecation: All cr_assume_arrays_* macros are deprecated in favor of + cr_assume_arr_*. + 2015-11-25 Franklin "Snaipe" Mathieu * criterion: version 2.1.1 diff --git a/README.md b/README.md index 85ec6944..4a2af083 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Snaipe/Criterion/blob/master/LICENSE) [![Version](https://img.shields.io/github/tag/Snaipe/Criterion.svg?label=version&style=flat)](https://github.com/Snaipe/Criterion/releases) +![Analytics](https://ga-beacon.appspot.com/UA-68371536-1/Criterion/README.md?pixel) + A dead-simple, yet extensible, C and C++ unit testing framework. ![Screencast](./doc/screencast.gif) @@ -39,10 +41,19 @@ the user would have with other frameworks: ## Downloads -* [Linux (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.1.0/criterion-v2.1.0-linux-x86_64.tar.bz2) -* [OS X (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.1.0/criterion-v2.1.0-osx-x86_64.tar.bz2) -* [Windows (MSVC - x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.1.0/criterion-v2.1.0-windows-msvc-x86_64.tar.bz2) -* [Windows (MinGW - x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.1.0/criterion-v2.1.0-windows-mingw-x86_64.tar.bz2) +### Packages + +* Mac OS X: `brew install snaipe/soft/criterion` +* [AUR](https://aur.archlinux.org/packages/criterion/): `yaourt -S criterion` + +### Binary archives + +* [Linux (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.2.0/criterion-v2.2.0-linux-x86_64.tar.bz2) +* [OS X (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.2.0/criterion-v2.2.0-osx-x86_64.tar.bz2) +* [Windows (MSVC - x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.2.0/criterion-v2.2.0-windows-msvc-x86_64.tar.bz2) +* [Windows (MinGW - x86_64)](https://github.com/Snaipe/Criterion/releases/download/v2.2.0/criterion-v2.2.0-windows-mingw-x86_64.tar.bz2) + +[comment]: # (Don't forget to change x86_64 to x64 on windows links on the next release) If you have a different platform, you can still [build the library from source](http://criterion.readthedocs.org/en/latest/setup.html#installation) @@ -66,27 +77,13 @@ Sample tests can be found in the [sample directory][samples]. ### Getting help -Gitter.im chat room: [![Join the chat at https://gitter.im/Snaipe/Criterion](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Snaipe/Criterion?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +Gitter.im chat room: [![Join the chat at https://gitter.im/Snaipe/Criterion](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Snaipe/Criterion?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +IRC channel: [#criterion][irc-chan] on irc.freenode.net ### Misc * [CMake find module for Criterion][find-module] -## F.A.Q. - -**Q. What's wrong with other C test frameworks?** -A. I worked with CUnit and Check, and I must say that they do their job - very well -- the only thing that bugs me is that setting up a test - suite from scratch is a pain, it should really be simpler. Most - (if not all) high-level languages have test frameworks with automatic - test registration, but all the ones for C require you to set up a - main, manually register suites, then tests. Criterion tries to - fix these shortcomings. - -**Q. Where has this been tested?** -A. Currently, on Linux 2.6.32 and Linux 3.15.7, although it should work on - most \*nix systems; Mac OS X Yosemite 10.10, FreeBSD 10.0, Windows 7 and 2K. - ## Credits Logo done by [Greehm](http://www.cargocollective.com/pbouigue) @@ -105,3 +102,5 @@ Logo done by [Greehm](http://www.cargocollective.com/pbouigue) [sample-report]: ./samples/report.c [find-module]: ./dev/FindCriterion.cmake + +[irc-chan]: http://webchat.freenode.net/?channels=%23criterion&uio=MTY9dHJ1ZSYyPXRydWUmOT10cnVlJjExPTE5NQ4e diff --git a/appveyor.yml b/appveyor.yml index 46eb294b..a7f13338 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.1.0_b{build}-{branch} +version: 2.2.0_b{build}-{branch} os: Visual Studio 2015 @@ -15,14 +15,17 @@ environment: matrix: - COMPILER: mingw GENERATOR: "MSYS Makefiles" + CXXFLAGS: -D__NO_INLINE__ - COMPILER: msvc GENERATOR: "Visual Studio 14 2015" + CFLAGS: /MP + CXXFLAGS: /MP clone_depth: 5 platform: - x86 - - x86_64 + - x64 configuration: - Debug @@ -38,8 +41,11 @@ install: - > cmake -Wno-dev + -DCTESTS=ON + -DI18N=OFF -DCMAKE_INSTALL_PREFIX="criterion-%RELEASE_NAME%" -DCMAKE_BUILD_TYPE="%CONFIGURATION%" + %CMAKE_OPTS% -G "%GENERATOR%" .. @@ -56,7 +62,11 @@ before_deploy: test_script: - cmake --build . --target criterion_tests - - ps: try { ctest } catch { type Testing/Temporary/LastTest.log } + - ps: | + ctest -j2 + if (-not $lastexitcode -eq 0) { + type Testing/Temporary/LastTest.log + } #after_test: # - 'make coveralls' diff --git a/debian.copyright b/debian.copyright new file mode 100644 index 00000000..6f6dc399 --- /dev/null +++ b/debian.copyright @@ -0,0 +1,25 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Criterion +Upstream-Contact: Franklin "Snaipe" Mathieu +Source: http://github.com/Snaipe/Criterion + +Files: * +Copyright: 2015, Franklin "Snaipe" Mathieu +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/dependencies/dyncall b/dependencies/dyncall index 68c57f66..180a5b77 160000 --- a/dependencies/dyncall +++ b/dependencies/dyncall @@ -1 +1 @@ -Subproject commit 68c57f664d4fabbc5b80327fbf5661a3a5a51e06 +Subproject commit 180a5b77ff95a17991afdc6e9be3501eca99ad36 diff --git a/dependencies/klib b/dependencies/klib new file mode 160000 index 00000000..cdb7e923 --- /dev/null +++ b/dependencies/klib @@ -0,0 +1 @@ +Subproject commit cdb7e9236dc47abf8da7ebd702cc6f7f21f0c502 diff --git a/dependencies/libcsptr b/dependencies/libcsptr index 2762164a..0d52904d 160000 --- a/dependencies/libcsptr +++ b/dependencies/libcsptr @@ -1 +1 @@ -Subproject commit 2762164acfaa712fea7dec6ed760ff88f7d2e026 +Subproject commit 0d52904da5d7bd0a3eac3c47e9f9bb10cd78a26e diff --git a/dependencies/valgrind/include/valgrind/valgrind.h b/dependencies/valgrind/include/valgrind/valgrind.h new file mode 100644 index 00000000..04c73ec8 --- /dev/null +++ b/dependencies/valgrind/include/valgrind/valgrind.h @@ -0,0 +1,6588 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2013 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 10 + + +#include +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_arm64_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + + +#if defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ + || defined(__CYGWIN32__) \ + || (defined(_WIN32) && defined(_M_IX86)) +# define PLAT_x86_win32 1 +#elif defined(__MINGW64__) \ + || (defined(_WIN64) && defined(_M_X64)) +# define PLAT_amd64_win64 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 +/* Big Endian uses ELF version 1 */ +# define PLAT_ppc64be_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 +/* Little Endian uses ELF version 2 */ +# define PLAT_ppc64le_linux 1 +#elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) +# define PLAT_arm_linux 1 +#elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) +# define PLAT_arm64_linux 1 +#elif defined(__linux__) && defined(__s390__) && defined(__s390x__) +# define PLAT_s390x_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==64) +# define PLAT_mips64_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips!=64) +# define PLAT_mips32_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +/* + * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client + * request. Accepts both pointers and integers as arguments. + * + * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind + * client request that does not return a value. + + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. Accepts + * both pointers and integers as arguments. Note that such calls are not + * necessarily pure functions -- they may have side effects. + */ + +#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ + _zzq_request, _zzq_arg1, _zzq_arg2, \ + _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ + _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgl %%edi,%%edi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ + (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ + (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ + (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) + +static __inline uintptr_t +valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, + uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, + uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, + uintptr_t _zzq_arg5) +{ + volatile uintptr_t _zzq_args[6]; + volatile unsigned int _zzq_result; + _zzq_args[0] = (uintptr_t)(_zzq_request); + _zzq_args[1] = (uintptr_t)(_zzq_arg1); + _zzq_args[2] = (uintptr_t)(_zzq_arg2); + _zzq_args[3] = (uintptr_t)(_zzq_arg3); + _zzq_args[4] = (uintptr_t)(_zzq_arg4); + _zzq_args[5] = (uintptr_t)(_zzq_arg5); + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default + __SPECIAL_INSTRUCTION_PREAMBLE + /* %EDX = client_request ( %EAX ) */ + __asm xchg ebx,ebx + __asm mov _zzq_result, edx + } + return _zzq_result; +} + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm xchg edi,edi \ + } \ + } while (0) + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || (defined(PLAT_amd64_win64) && defined(__GNUC__)) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgq %%rdi,%%rdi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------- amd64-Win64 ------------------------- */ + +#if defined(PLAT_amd64_win64) && !defined(__GNUC__) + +#error Unsupported compiler. + +#endif /* PLAT_amd64_win64 */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ + "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +#if defined(PLAT_ppc64le_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R12 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr r9, r9, r9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------- */ + +#if defined(PLAT_arm64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ + "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("mov x3, %1\n\t" /*default*/ \ + "mov x4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = client_request ( X4 ) */ \ + "orr x10, x10, x10\n\t" \ + "mov %0, x3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "x3", "x4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = guest_NRADDR */ \ + "orr x11, x11, x11\n\t" \ + "mov %0, x3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "x3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir X8 */ \ + "orr x12, x12, x12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr x9, x9, x9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------ s390x-linux ------------------------ */ + +#if defined(PLAT_s390x_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific + * code. This detection is implemented in platform specific toIR.c + * (e.g. VEX/priv/guest_s390_decoder.c). + */ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "lr 15,15\n\t" \ + "lr 1,1\n\t" \ + "lr 2,2\n\t" \ + "lr 3,3\n\t" + +#define __CLIENT_REQUEST_CODE "lr 2,2\n\t" +#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" +#define __CALL_NO_REDIR_CODE "lr 4,4\n\t" +#define __VEX_INJECT_IR_CODE "lr 5,5\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(/* r2 = args */ \ + "lgr 2,%1\n\t" \ + /* r3 = default */ \ + "lgr 3,%2\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CLIENT_REQUEST_CODE \ + /* results = r3 */ \ + "lgr %0, 3\n\t" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "2", "3", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __GET_NR_CONTEXT_CODE \ + "lgr %0, 3\n\t" \ + : "=a" (__addr) \ + : \ + : "cc", "3", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_R1 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CALL_NO_REDIR_CODE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __VEX_INJECT_IR_CODE); \ + } while (0) + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ---------------- */ + +#if defined(PLAT_mips32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +/* .word 0x342 + * .word 0x742 + * .word 0xC2 + * .word 0x4C2*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "srl $0, $0, 13\n\t" \ + "srl $0, $0, 29\n\t" \ + "srl $0, $0, 3\n\t" \ + "srl $0, $0, 19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* T3 = client_request ( T4 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %t9 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%t9 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ---------------- */ + +#if defined(PLAT_mips64_linux) + +typedef + struct { + unsigned long long nraddr; /* where's the code? */ + } + OrigFn; + +/* dsll $0,$0, 3 + * dsll $0,$0, 13 + * dsll $0,$0, 29 + * dsll $0,$0, 19*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ + "dsll $0,$0,29 ; dsll $0,$0,19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = client_request ( $12 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts + the default behaviour equivalance class tag "0000" into the name. + See pub_tool_redir.h for details -- normally you don't need to + think about this, though. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Also provide end-user facilities for function replacement, rather + than wrapping. A replacement function differs from a wrapper in + that it has no way to get hold of the original function being + called, and hence no way to call onwards to it. In a replacement + function, VALGRIND_GET_ORIG_FN always returns zero. */ + +#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) + +#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movl %%esp,%%edi\n\t" \ + "andl $0xfffffff0,%%esp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movl %%edi,%%esp\n\t" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movq %%rsp,%%r14\n\t" \ + "andq $0xfffffffffffffff0,%%rsp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movq %%r14,%%rsp\n\t" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rlwinm 1,1,0,0,27\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +/* ------------------------- ppc64le-linux ----------------------- */ +#if defined(PLAT_ppc64le_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(12)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +/* This is a bit tricky. We store the original stack pointer in r10 + as it is callee-saves. gcc doesn't allow the use of r11 for some + reason. Also, we can't directly "bic" the stack pointer in thumb + mode since r13 isn't an allowed register number in that context. + So use r4 as a temporary, since that is about to get trashed + anyway, just after each use of this macro. Side effect is we need + to be very careful about any future changes, since + VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ +#define VALGRIND_ALIGN_STACK \ + "mov r10, sp\n\t" \ + "mov r4, sp\n\t" \ + "bic r4, r4, #7\n\t" \ + "mov sp, r4\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, r10\n\t" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------ */ + +#if defined(PLAT_arm64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ + "x18", "x19", "x20", "x30", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ + "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ + "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ + "v26", "v27", "v28", "v29", "v30", "v31" + +/* x21 is callee-saved, so we can use it to save and restore SP around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mov x21, sp\n\t" \ + "bic sp, x21, #15\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, x21\n\t" + +/* These CALL_FN_ macros assume that on arm64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1, #96] \n\t" \ + "str x8, [sp, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------- s390x-linux ------------------------- */ + +#if defined(PLAT_s390x_linux) + +/* Similar workaround as amd64 (see above), but we use r11 as frame + pointer and save the old r11 in r7. r11 might be used for + argvec, therefore we copy argvec in r1 since r1 is clobbered + after the call anyway. */ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"d"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + ".cfi_remember_state\n\t" \ + "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ + "lgr 7,11\n\t" \ + "lgr 11,%2\n\t" \ + ".cfi_def_cfa r11, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "lgr 11, 7\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE \ + "lgr 1,%1\n\t" +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Nb: On s390 the stack pointer is properly aligned *at all times* + according to the s390 GCC maintainer. (The ABI specification is not + precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and + VALGRIND_RESTORE_STACK are not defined here. */ + +/* These regs are trashed by the hidden call. Note that we overwrite + r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the + function a proper return address. All others are ABI defined call + clobbers. */ +#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ + "f0","f1","f2","f3","f4","f5","f6","f7" + +/* Nb: Although r11 is modified in the asm snippets below (inside + VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for + two reasons: + (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not + modified + (2) GCC will complain that r11 cannot appear inside a clobber section, + when compiled with -O -fno-omit-frame-pointer + */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 1, 0(1)\n\t" /* target->r1 */ \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +/* The call abi has the arguments in r2-r6 and stack */ +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1, arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-168\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,168\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-176\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,176\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-184\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,184\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-192\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,192\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-200\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,200\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-208\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,208\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-216\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "mvc 208(8,15), 96(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,216\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ----------------------- */ + +#if defined(PLAT_mips32_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16\n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" /* arg1*/ \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 24\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 24 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "nop\n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 56\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 48(%1) \n\t" \ + "sw $4, 44($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 56 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ------------------------- */ + +#if defined(PLAT_mips64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" /* arg1*/ \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "dsubu $29, $29, 8\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 8\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "dsubu $29, $29, 16\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 16\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "dsubu $29, $29, 24\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 24\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "dsubu $29, $29, 32\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 96(%1)\n\t" \ + "sd $4, 24($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 32\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips64_linux */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* Allows the client program and/or gdbserver to execute a monitor + command. */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, + + /* Disable/enable error reporting level. Takes a single + Word arg which is the delta to this thread's error + disablement indicator. Hence 1 disables or further + disables errors, and -1 moves back towards enablement. + Other values are not allowed. */ + VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, + + /* Initialise IR injection */ + VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0) + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(CR_UNUSED const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(CR_UNUSED const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For + Memcheck, it does four things: + + - It records that the size of a block has been changed. This assumes that + the block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - If the block shrunk, it marks the freed memory as being unaddressable. + + - If the block grew, it marks the new area as undefined and defines a red + zone past the end of the new block. + + - The V-bits of the overlap between the old and the new block are preserved. + + VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block + and before deallocation of the old block. + + In many cases, these three client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ + addr, oldSizeB, newSizeB, rzB, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0) + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0) + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0) + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0) + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0) + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0) + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0) + +/* Mark a piece of memory as being a stack. Returns a stack id. + start is the lowest addressable stack byte, end is the highest + addressable stack byte. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0) + +/* Change the start and end address of the stack id. + start is the new lowest addressable stack byte, end is the new highest + addressable stack byte. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0) + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0) + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0) + +/* Disable error reporting for this thread. Behaves in a stack like + way, so you can safely call this multiple times provided that + VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times + to re-enable reporting. The first call of this macro disables + reporting. Subsequent calls have no effect except to increase the + number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable + reporting. Child threads do not inherit this setting from their + parents -- they are always created with reporting enabled. */ +#define VALGRIND_DISABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + 1, 0, 0, 0, 0) + +/* Re-enable error reporting, as per comments on + VALGRIND_DISABLE_ERROR_REPORTING. */ +#define VALGRIND_ENABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + -1, 0, 0, 0, 0) + +/* Execute a monitor command from the client program. + If a connection is opened with GDB, the output will be sent + according to the output mode set for vgdb. + If no connection is opened, output will go to the log output. + Returns 1 if command not recognised, 0 otherwise. */ +#define VALGRIND_MONITOR_COMMAND(command) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ + command, 0, 0, 0, 0) + + +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + +#endif /* __VALGRIND_H */ diff --git a/description.txt b/description.txt new file mode 100644 index 00000000..e249add8 --- /dev/null +++ b/description.txt @@ -0,0 +1,23 @@ +Criterion is a dead-simple, yet extensible, C and C++ unit testing framework. + +Most test frameworks for C require a lot of boilerplate code to +set up tests and test suites -- you need to create a main, +then register new test suites, then register the tests within +these suits, and finally call the right functions. + +This gives the user great control, at the unfortunate cost of simplicity. + +Criterion follows the KISS principle, while keeping the control +the user would have with other frameworks: + +* C99 and C++11 compatible. +* Tests are automatically registered when declared. +* Implements a xUnit framework structure. +* A default entry point is provided, no need to declare a main + unless you want to do special handling. +* Test are isolated in their own process, crashes and signals can be + reported and tested. +* Unified interface between C and C++: include the criterion header and it *just* works. +* Supports parameterized tests and theories. +* Progress and statistics can be followed in real time with report hooks. +* TAP output format can be enabled with an option. diff --git a/doc/assert.rst b/doc/assert.rst index 46ad0044..49d23341 100644 --- a/doc/assert.rst +++ b/doc/assert.rst @@ -9,36 +9,39 @@ As each ``assert`` macros have an ``expect`` counterpart with the exact same number of parameters and name suffix, there is no benefit in adding ``expect`` macros to this list. Hence only ``assert`` macros are represented here. +All ``assert`` macros may take an optional ``printf`` format string and +parameters. + Common Assertions ----------------- -======================================================================= =========================================================================== =========================================== -Macro Passes if and only if Notes -======================================================================= =========================================================================== =========================================== -cr_assert(Condition, [Message, [Args...]]) ``Condition`` is true. ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_not(Condition, [Message, [Args...]]) ``Condition`` is false. ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_null(Value, [Message, [Args...]]) ``Value`` is ``NULL``. ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_not_null(Value, [Message, [Args...]]) ``Value`` is not ``NULL``. ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_eq(Actual, Expected, [Message, [Args...]]) ``Actual`` is equal to ``Expected``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_neq(Actual, Unexpected, [Message, [Args...]]) ``Actual`` is not equal to ``Unexpected``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_lt(Actual, Reference, [Message, [Args...]]) ``Actual`` is less than ``Reference``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_leq(Actual, Reference, [Message, [Args...]]) ``Actual`` is less or equal to ``Reference``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_gt(Actual, Reference, [Message, [Args...]]) ``Actual`` is greater than ``Reference``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_geq(Actual, Reference, [Message, [Args...]]) ``Actual`` is greater or equal to ``Reference``. Compatible with C++ operator overloading ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_float_eq(Actual, Expected, Epsilon, [Message, [Args...]]) ``Actual`` is equal to ``Expected`` with a tolerance of ``Epsilon``. Use this to test equality between floats ------------------------------------------------------------------------ --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_float_neq(Actual, Unexpected, Epsilon, [Message, [Args...]]) ``Actual`` is not equal to ``Unexpected`` with a tolerance of ``Epsilon``. Use this to test inequality between floats -======================================================================= =========================================================================== =========================================== +=========================================================================== =========================================================================== =========================================== +Macro Passes if and only if Notes +=========================================================================== =========================================================================== =========================================== +cr_assert(Condition, [FormatString, [Args...]]) ``Condition`` is true. +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_not(Condition, [FormatString, [Args...]]) ``Condition`` is false. +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_null(Value, [FormatString, [Args...]]) ``Value`` is ``NULL``. +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_not_null(Value, [FormatString, [Args...]]) ``Value`` is not ``NULL``. +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_eq(Actual, Expected, [FormatString, [Args...]]) ``Actual`` is equal to ``Expected``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_neq(Actual, Unexpected, [FormatString, [Args...]]) ``Actual`` is not equal to ``Unexpected``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_lt(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is less than ``Reference``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_leq(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is less or equal to ``Reference``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_gt(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is greater than ``Reference``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_geq(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is greater or equal to ``Reference``. Compatible with C++ operator overloading +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_float_eq(Actual, Expected, Epsilon, [FormatString, [Args...]]) ``Actual`` is equal to ``Expected`` with a tolerance of ``Epsilon``. Use this to test equality between floats +--------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_float_neq(Actual, Unexpected, Epsilon, [FormatString, [Args...]]) ``Actual`` is not equal to ``Unexpected`` with a tolerance of ``Epsilon``. Use this to test inequality between floats +=========================================================================== =========================================================================== =========================================== String Assertions ----------------- @@ -47,98 +50,98 @@ Note: these macros are meant to deal with *native* strings, i.e. char arrays. Most of them won't work on ``std::string`` in C++, with some exceptions -- for ``std::string``, you should use regular comparison assersions, as listed above. -=========================================================== =================================================================== =========================================== -Macro Passes if and only if Notes -=========================================================== =================================================================== =========================================== -cr_assert_str_empty(Value, [Message, [Args...]]) ``Value`` is an empty string. Also works on std::string ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_not_empty(Value, [Message, [Args...]]) ``Value`` is not an empty string. Also works on std::string ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_eq(Actual, Expected, [Message, [Args...]]) ``Actual`` is lexicographically equal to ``Expected``. ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_neq(Actual, Unexpected, [Message, [Args...]]) ``Actual`` is not lexicographically equal to ``Unexpected``. ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_lt(Actual, Reference, [Message, [Args...]]) ``Actual`` is lexicographically less than ``Reference``. ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_leq(Actual, Reference, [Message, [Args...]]) ``Actual`` is lexicographically less or equal to ``Reference``. ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_gt(Actual, Reference, [Message, [Args...]]) ``Actual`` is lexicographically greater than ``Reference``. ------------------------------------------------------------ ------------------------------------------------------------------- ------------------------------------------- -cr_assert_str_geq(Actual, Reference, [Message, [Args...]]) ``Actual`` is lexicographically greater or equal to ``Reference``. -=========================================================== =================================================================== =========================================== +================================================================ =================================================================== =========================================== +Macro Passes if and only if Notes +================================================================ =================================================================== =========================================== +cr_assert_str_empty(Value, [FormatString, [Args...]]) ``Value`` is an empty string. Also works on std::string +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_not_empty(Value, [FormatString, [Args...]]) ``Value`` is not an empty string. Also works on std::string +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_eq(Actual, Expected, [FormatString, [Args...]]) ``Actual`` is lexicographically equal to ``Expected``. +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_neq(Actual, Unexpected, [FormatString, [Args...]]) ``Actual`` is not lexicographically equal to ``Unexpected``. +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_lt(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is lexicographically less than ``Reference``. +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_leq(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is lexicographically less or equal to ``Reference``. +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_gt(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is lexicographically greater than ``Reference``. +---------------------------------------------------------------- ------------------------------------------------------------------- ------------------------------------------- +cr_assert_str_geq(Actual, Reference, [FormatString, [Args...]]) ``Actual`` is lexicographically greater or equal to ``Reference``. +================================================================ =================================================================== =========================================== Array Assertions ----------------- -=========================================================================== =========================================================================== =========================================== -Macro Passes if and only if Notes -=========================================================================== =========================================================================== =========================================== -cr_assert_arr_eq(Actual, Expected, [Message, [Args...]]) ``Actual`` is byte-to-byte equal to ``Expected``. This should not be used on struct arrays, - consider using ``cr_assert_arr_eq_cmp`` - instead. ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_neq(Actual, Unexpected, [Message, [Args...]]) ``Actual`` is not byte-to-byte equal to ``Unexpected``. This should not be used on struct arrays, - consider using ``cr_assert_arr_neq_cmp`` - instead. ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_eq_cmp(Actual, Expected, Size, Cmp, [Message, [Args...]]) ``Actual`` is comparatively equal to ``Expected`` Only available in C++ and GNU C99 ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_neq_cmp(Actual, Unexpected, Size, Cmp, [Message, [Args...]]) ``Actual`` is not comparatively equal to ``Expected`` Only available in C++ and GNU C99 ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_lt_cmp(Actual, Reference, Size, Cmp, [Message, [Args...]]) ``Actual`` is comparatively less than ``Reference`` Only available in C++ and GNU C99 ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_leq_cmp(Actual, Reference, Size, Cmp, [Message, [Args...]]) ``Actual`` is comparatively less or equal to ``Reference`` Only available in C++ and GNU C99 ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_gt_cmp(Actual, Reference, Size, Cmp, [Message, [Args...]]) ``Actual`` is comparatively greater than ``Reference`` Only available in C++ and GNU C99 ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_arr_geq_cmp(Actual, Reference, Size, Cmp, [Message, [Args...]]) ``Actual`` is comparatively greater or equal to ``Reference`` Only available in C++ and GNU C99 -=========================================================================== =========================================================================== =========================================== +=============================================================================== =========================================================================== =========================================== +Macro Passes if and only if Notes +=============================================================================== =========================================================================== =========================================== +cr_assert_arr_eq(Actual, Expected, [FormatString, [Args...]]) ``Actual`` is byte-to-byte equal to ``Expected``. This should not be used on struct arrays, + consider using ``cr_assert_arr_eq_cmp`` + instead. +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_neq(Actual, Unexpected, [FormatString, [Args...]]) ``Actual`` is not byte-to-byte equal to ``Unexpected``. This should not be used on struct arrays, + consider using ``cr_assert_arr_neq_cmp`` + instead. +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_eq_cmp(Actual, Expected, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is comparatively equal to ``Expected`` Only available in C++ and GNU C99 +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_neq_cmp(Actual, Unexpected, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is not comparatively equal to ``Expected`` Only available in C++ and GNU C99 +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_lt_cmp(Actual, Reference, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is comparatively less than ``Reference`` Only available in C++ and GNU C99 +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_leq_cmp(Actual, Reference, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is comparatively less or equal to ``Reference`` Only available in C++ and GNU C99 +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_gt_cmp(Actual, Reference, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is comparatively greater than ``Reference`` Only available in C++ and GNU C99 +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_arr_geq_cmp(Actual, Reference, Size, Cmp, [FormatString, [Args...]]) ``Actual`` is comparatively greater or equal to ``Reference`` Only available in C++ and GNU C99 +=============================================================================== =========================================================================== =========================================== Exception Assertions -------------------- The following assertion macros are only defined for C++. -=========================================================================== =========================================================================== =========================================== -Macro Passes if and only if Notes -=========================================================================== =========================================================================== =========================================== -cr_assert_throw(Statement, Exception, [Message, [Args...]]) ``Statement`` throws an instance of ``Exception``. ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_no_throw(Statement, Exception, [Message, [Args...]]) ``Statement`` does not throws an instance of ``Exception``. ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_any_throw(Statement, [Message, [Args...]]) ``Statement`` throws any kind of exception. ---------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- -cr_assert_none_throw(Statement, [Message, [Args...]]) ``Statement`` does not throw any exception. -=========================================================================== =========================================================================== =========================================== +=============================================================================== =========================================================================== =========================================== +Macro Passes if and only if Notes +=============================================================================== =========================================================================== =========================================== +cr_assert_throw(Statement, Exception, [FormatString, [Args...]]) ``Statement`` throws an instance of ``Exception``. +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_no_throw(Statement, Exception, [FormatString, [Args...]]) ``Statement`` does not throws an instance of ``Exception``. +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_any_throw(Statement, [FormatString, [Args...]]) ``Statement`` throws any kind of exception. +------------------------------------------------------------------------------- --------------------------------------------------------------------------- ------------------------------------------- +cr_assert_none_throw(Statement, [FormatString, [Args...]]) ``Statement`` does not throw any exception. +=============================================================================== =========================================================================== =========================================== File Assertions --------------- -=============================================================================== ============================================================================ =========================================== -Macro Passes if and only if Notes -=============================================================================== ============================================================================ =========================================== -cr_assert_file_contents_eq_str(File, ExpectedContents, [Message, [Args...]]) The contents of ``File`` are equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_file_contents_neq_str(File, ExpectedContents, [Message, [Args...]]) The contents of ``File`` are not equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stdout_eq_str(ExpectedContents, [Message, [Args...]]) The contents of ``stdout`` are equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stdout_neq_str(ExpectedContents, [Message, [Args...]]) The contents of ``stdout`` are not equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stderr_eq_str(ExpectedContents, [Message, [Args...]]) The contents of ``stderr`` are equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stderr_neq_str(ExpectedContents, [Message, [Args...]]) The contents of ``stderr`` are not equal to the string ``ExpectedContents``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_file_contents_eq(File, RefFile, [Message, [Args...]]) The contents of ``File`` are equal to the contents of ``RefFile``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_file_contents_neq(File, RefFile, [Message, [Args...]]) The contents of ``File`` are not equal to the contents of ``RefFile``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stdout_eq(RefFile, [Message, [Args...]]) The contents of ``stdout`` are equal to the contents of ``RefFile``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stdout_neq(RefFile, [Message, [Args...]]) The contents of ``stdout`` are not equal to the contents of ``RefFile``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stderr_eq(RefFile, [Message, [Args...]]) The contents of ``stderr`` are equal to the contents of ``RefFile``. -------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- -cr_assert_stderr_neq(RefFile, [Message, [Args...]]) The contents of ``stderr`` are not equal to the contents of ``RefFile``. -=============================================================================== ============================================================================ =========================================== +=================================================================================== ============================================================================ =========================================== +Macro Passes if and only if Notes +=================================================================================== ============================================================================ =========================================== +cr_assert_file_contents_eq_str(File, ExpectedContents, [FormatString, [Args...]]) The contents of ``File`` are equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_file_contents_neq_str(File, ExpectedContents, [FormatString, [Args...]]) The contents of ``File`` are not equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stdout_eq_str(ExpectedContents, [FormatString, [Args...]]) The contents of ``stdout`` are equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stdout_neq_str(ExpectedContents, [FormatString, [Args...]]) The contents of ``stdout`` are not equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stderr_eq_str(ExpectedContents, [FormatString, [Args...]]) The contents of ``stderr`` are equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stderr_neq_str(ExpectedContents, [FormatString, [Args...]]) The contents of ``stderr`` are not equal to the string ``ExpectedContents``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_file_contents_eq(File, RefFile, [FormatString, [Args...]]) The contents of ``File`` are equal to the contents of ``RefFile``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_file_contents_neq(File, RefFile, [FormatString, [Args...]]) The contents of ``File`` are not equal to the contents of ``RefFile``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stdout_eq(RefFile, [FormatString, [Args...]]) The contents of ``stdout`` are equal to the contents of ``RefFile``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stdout_neq(RefFile, [FormatString, [Args...]]) The contents of ``stdout`` are not equal to the contents of ``RefFile``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stderr_eq(RefFile, [FormatString, [Args...]]) The contents of ``stderr`` are equal to the contents of ``RefFile``. +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------- ------------------------------------------- +cr_assert_stderr_neq(RefFile, [FormatString, [Args...]]) The contents of ``stderr`` are not equal to the contents of ``RefFile``. +=================================================================================== ============================================================================ =========================================== diff --git a/doc/conf.py b/doc/conf.py index b7b4615c..f348be2e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -39,7 +39,7 @@ # built documents. # # The short X.Y version. -version = '2.1.0' +version = '2.2.0' # The full version, including alpha/beta/rc tags. release = version diff --git a/doc/env.rst b/doc/env.rst index 745ff9ba..e980f71d 100644 --- a/doc/env.rst +++ b/doc/env.rst @@ -8,11 +8,14 @@ Command line arguments ---------------------- * ``-h or --help``: Show a help message with the available switches. +* ``-q or --quiet``: Disables all logging. * ``-v or --version``: Prints the version of criterion that has been linked against. * ``-l or --list``: Print all the tests in a list. * ``-f or --fail-fast``: Exit after the first test failure. * ``--ascii``: Don't use fancy unicode symbols or colors in the output. +* ``-jN or --jobs N``: Use ``N`` parallel jobs to run the tests. ``0`` picks + a number of jobs ideal for your hardware configuration. * ``--pattern [PATTERN]``: Run tests whose string identifier matches the given shell wildcard pattern (see dedicated section below). (\*nix only) * ``--no-early-exit``: The test workers shall not prematurely exit when done and @@ -20,30 +23,53 @@ Command line arguments This is useful when tracking memory leaks with ``valgrind --tool=memcheck``. * ``-S or --short-filename``: The filenames are displayed in their short form. * ``--always-succeed``: The process shall exit with a status of ``0``. -* ``--tap``: Enables the TAP (Test Anything Protocol) output format. +* ``--tap[=FILE]``: Writes a TAP (Test Anything Protocol) report to FILE. + No file or ``"-"`` means ``stderr`` and implies ``--quiet``. This option is + equivalent to ``--output=tap:FILE``. +* ``--xml[=FILE]``: Writes JUnit4 XML report to FILE. + No file or ``"-"`` means ``stderr`` and implies ``--quiet``. This option is + equivalent to ``--output=tap:FILE``. +* ``--json[=FILE]``: Writes a JSON report to FILE. + No file or ``"-"`` means ``stderr`` and implies ``--quiet``. This option is + equivalent to ``--output=tap:FILE``. * ``--verbose[=level]``: Makes the output verbose. When provided with an integer, sets the verbosity level to that integer. +* ``-OPROVIDER:FILE or --output=PROVIDER:FILE``: Write a test report to FILE + using the output provider named by PROVIDER. + If FILE is ``"-"``, it implies ``--quiet``, and the report shall be written + to ``stderr``. Shell Wildcard Pattern ---------------------- -Patterns in criterion are matched against a test's string identifier with -``fnmatch``. This feature is only available on \*nix systems where ``fnmatch`` -is provided. +Extglob patterns in criterion are matched against a test's string identifier. +This feature is only available on \*nix systems where ``PCRE`` is provided. -Special characters used in shell-style wildcard patterns are: +In the table below, a ``pattern-list`` is a list of patterns separated by ``|``. +Any extglob pattern can be constructed by combining any of the following +sub-patterns: -=========== =================================== -Pattern Meaning -=========== =================================== -``*`` matches everything ------------ ----------------------------------- -``?`` matches any character ------------ ----------------------------------- -``[seq]`` matches any character in *seq* ------------ ----------------------------------- -``[!seq]`` matches any character not in *seq* -=========== =================================== +==================== ====================================================== +Pattern Meaning +==================== ====================================================== +``*`` matches everything +-------------------- ------------------------------------------------------ +``?`` matches any character +-------------------- ------------------------------------------------------ +``[seq]`` matches any character in *seq* +-------------------- ------------------------------------------------------ +``[!seq]`` matches any character not in *seq* +-------------------- ------------------------------------------------------ +``?(pattern-list)`` Matches zero or one occurrence of the given patterns +-------------------- ------------------------------------------------------ +``*(pattern-list)`` Matches zero or more occurrences of the given patterns +-------------------- ------------------------------------------------------ +``+(pattern-list)`` Matches one or more occurrences of the given patterns +-------------------- ------------------------------------------------------ +``@(pattern-list)`` Matches one of the given patterns +-------------------- ------------------------------------------------------ +``!(pattern-list)`` Matches anything except one of the given patterns +==================== ====================================================== A test string identifier is of the form ``suite-name/test-name``, so a pattern of ``simple/*`` matches every tests in the ``simple`` suite, ``*/passing`` @@ -57,11 +83,19 @@ Environment variables are alternatives to command line switches when set to 1. * ``CRITERION_ALWAYS_SUCCEED``: Same as ``--always-succeed``. * ``CRITERION_NO_EARLY_EXIT``: Same as ``--no-early-exit``. -* ``CRITERION_ENABLE_TAP``: Same as ``--tap``. * ``CRITERION_FAIL_FAST``: Same as ``--fail-fast``. * ``CRITERION_USE_ASCII``: Same as ``--ascii``. +* ``CRITERION_JOBS``: Same as ``--jobs``. Sets the number of jobs to + its value. * ``CRITERION_SHORT_FILENAME``: Same as ``--short-filename``. * ``CRITERION_VERBOSITY_LEVEL``: Same as ``--verbose``. Sets the verbosity level to its value. * ``CRITERION_TEST_PATTERN``: Same as ``--pattern``. Sets the test pattern to its value. (\*nix only) +* ``CRITERION_DISABLE_TIME_MEASUREMENTS``: Disables any time measurements on + the tests. +* ``CRITERION_OUTPUTS``: Can be set to a comma-separated list of + ``PROVIDER:FILE`` entries. For instance, setting the variable to + ``tap:foo.tap,xml:bar.xml`` has the same effect as specifying ``--tap=foo.tap`` + and ``--xml=bar.xml`` at once. +* ``CRITERION_ENABLE_TAP``: (Deprecated, use CRITERION_OUTPUTS) Same as ``--tap``. diff --git a/doc/index.rst b/doc/index.rst index 1b2183b3..edb8e4c3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,6 +10,7 @@ Criterion assert hooks env + output parameterized theories internal diff --git a/doc/internal.rst b/doc/internal.rst index 69b5f0ad..8330590e 100644 --- a/doc/internal.rst +++ b/doc/internal.rst @@ -28,7 +28,7 @@ Field Type Description =================== ================================== ============================================================== logging_threshold enum criterion_logging_level The logging level ------------------- ---------------------------------- -------------------------------------------------------------- -output_provider struct criterion_output_provider * The output provider (see below) +logger struct criterion_logger * The logger (see below) ------------------- ---------------------------------- -------------------------------------------------------------- no_early_exit bool True iff the test worker should exit early ------------------- ---------------------------------- -------------------------------------------------------------- @@ -66,24 +66,23 @@ Example main int main(int argc, char *argv[]) { struct criterion_test_set *tests = criterion_initialize(); - if (!criterion_handle_args(argc, argv, true)) - return 0; - - int result = !criterion_run_all_tests(set); + int result = 0; + if (criterion_handle_args(argc, argv, true)) + result = !criterion_run_all_tests(set); criterion_finalize(set); return result; } -Implementing your own output provider -------------------------------------- +Implementing your own logger +---------------------------- -In case you are not satisfied by the default output provider, you can implement -yours. To do so, simply set the ``output_provider`` option to your custom -output provider. +In case you are not satisfied by the default logger, you can implement +yours. To do so, simply set the ``logger`` option to your custom +logger. Each function contained in the structure is called during one of the standard phase of the criterion runner. -For more insight on how to implement this, see other existing output providers +For more insight on how to implement this, see other existing loggers in ``src/log/``. diff --git a/doc/intro.rst b/doc/intro.rst index e2a11250..5d2792e9 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -1,8 +1,7 @@ Introduction ============ -Criterion is a dead-simple, non-intrusive testing framework for the C -programming language. +Criterion is a dead-simple, non-intrusive unit testing framework for C and C++. Philosophy ---------- @@ -20,12 +19,15 @@ the user would have with other frameworks. Features -------- +* C99 and C++11 compatible. * Tests are automatically registered when declared. +* Implements a xUnit framework structure. * A default entry point is provided, no need to declare a main unless you want to do special handling. * Test are isolated in their own process, crashes and signals can be reported and tested. +* Unified interface between C and C++: include the criterion header and it *just* works. +* Supports parameterized tests and theories. * Progress and statistics can be followed in real time with report hooks. * TAP output format can be enabled with an option. -* Runs on Linux, FreeBSD, Mac OS X, and Windows (Compiling with MinGW GCC). -* xUnit framework structure +* Runs on Linux, FreeBSD, Mac OS X, and Windows (Compiling with MinGW GCC and Visual Studio 2015+). diff --git a/doc/output.rst b/doc/output.rst new file mode 100644 index 00000000..482a6315 --- /dev/null +++ b/doc/output.rst @@ -0,0 +1,44 @@ +Writing tests reports in a custom format +======================================== + +Outputs providers are used to write tests reports in the format of your choice: +for instance, TAP and XML reporting are implemented with output providers. + +Adding a custom output provider +------------------------------- + +An output provider is a function with the following signature: + +.. code-block:: c + + void func(FILE *out, struct criterion_global_stats *stats); + +Once implemented, you then need to register it as an output provider: + +.. code-block:: c + + criterion_register_output_provider("provider name", func); + +This needs to be done before the test runner stops, so you may want to register +it either in a self-provided main, or in a PRE_ALL or POST_ALL report hook. + +Writing to a file with an output provider +----------------------------------------- + +To tell criterion to write a report to a specific file using the output provider +of your choice, you can either pass ``--output`` as a command-line +parameter: + +.. code-block:: bash + + ./my_tests --output="provider name":/path/to/file + +Or, you can do so directly by calling ``criterion_add_output`` before the +runner stops: + +.. code-block:: c + + criterion_add_output("provider name", "/path/to/file"); + +The path may be relative. If ``"-"`` is passed as a filename, the report will +be written to ``stderr``. diff --git a/doc/parameterized.rst b/doc/parameterized.rst index 89f251e9..e6b033e0 100644 --- a/doc/parameterized.rst +++ b/doc/parameterized.rst @@ -18,7 +18,7 @@ and the parameter generator function: #include - ParameterizedTestParameter(suite_name, test_name) = { + ParameterizedTestParameters(suite_name, test_name) { void *params; size_t nb_params; @@ -54,17 +54,12 @@ easily use a struct to hold the context as a workaround: ... }; - ParameterizedTestParameter(suite_name, test_name) = { - size_t nb_params = 32; - struct my_params *params = cr_malloc(sizeof (struct my_params) * nb_params); - - // generate parameter set - - params[0] = ... - params[1] = ... - - ... + ParameterizedTestParameters(suite_name, test_name) { + struct my_params params[] = { + // parameter set + }; + size_t nb_params = sizeof (params) / sizeof (struct my_params); return cr_make_param_array(struct my_params, params, nb_params); } @@ -72,8 +67,20 @@ easily use a struct to hold the context as a workaround: // access param.param0, param.param1, ... } -Dynamically allocating fields -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +C++ users can also use a simpler syntax before returning an array of parameters: + +.. code-block:: c++ + + ParameterizedTestParameters(suite_name, test_name) { + struct my_params params[] = { + // parameter set + }; + + return criterion_test_params(params); + } + +Dynamically allocating parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Any dynamic memory allocation done from a ParameterizedTestParameter function **must** be done with ``cr_malloc``, ``cr_calloc``, or ``cr_realloc``. @@ -100,6 +107,9 @@ use: ``criterion::new_arr``. The function possess the exact same semantics as ``delete[] array``. +Furthermore, the ``criterion::allocator`` allocator can be used with STL +containers to allocate memory with the functions above. + Freeing dynamically allocated parameter fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,15 +128,36 @@ the cleanup function that should be called on the generated parameter context: cr_free(((struct my_params *) ctp->params)->some_int_ptr); } - ParameterizedTestParameter(suite_name, test_name) = { - static my_params param = { + ParameterizedTestParameters(suite_name, test_name) { + static my_params params[] = {{ .some_int_ptr = cr_malloc(sizeof (int)); - }; - *param.some_int_ptr = 42; + }}; + param[0].some_int_ptr = 42; - return cr_make_param_array(struct my_params, ¶m, 1, cleanup_params); + return cr_make_param_array(struct my_params, params, 1, cleanup_params); } +C++ users can use a more convenient approach: + +.. code-block:: c++ + + #include + + struct my_params { + std::unique_ptr some_int_ptr; + + my_params(int *ptr) : some_int_ptr(ptr, criterion::free) {} + }; + + ParameterizedTestParameters(suite_name, test_name) { + static criterion::parameters params; + params.push_back(my_params(criterion::new_obj(42))); + + return params; + } + +``criterion::parameters`` is typedef'd as ``std::vector>``. + Configuring parameterized tests ------------------------------- diff --git a/doc/screencast.gif b/doc/screencast.gif index b8289416..8b61b503 100644 Binary files a/doc/screencast.gif and b/doc/screencast.gif differ diff --git a/doc/setup.rst b/doc/setup.rst index b4001e2b..4083fe66 100644 --- a/doc/setup.rst +++ b/doc/setup.rst @@ -7,7 +7,8 @@ Prerequisites The library is supported on Linux, OS X, FreeBSD, and Windows. The following compilers are supported to compile both the library and the tests: -* GCC 4.9+ + +* GCC 4.9+ (Can be relaxed to GCC 4.6+ when not using C++) * Clang 3.4+ * MSVC 14+ (Included in Visual Studio 2015 or later) diff --git a/doc/theories.rst b/doc/theories.rst index f95bae0d..4f07d8aa 100644 --- a/doc/theories.rst +++ b/doc/theories.rst @@ -78,26 +78,26 @@ Macro Description ------------------------------------------------------- ---------------------------------------------------- ``cr_assume_float_neq(Actual, Unexpected, Epsilon)`` Assumes Actual != Expected with an error of Epsilon. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_eq(Actual, Expected)`` Assumes Actual and Expected are the same string. +``cr_assume_str_eq(Actual, Expected)`` Assumes Actual and Expected are the same string. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_neq(Actual, Unexpected)`` Assumes Actual and Expected are not the same string. +``cr_assume_str_neq(Actual, Unexpected)`` Assumes Actual and Expected are not the same string. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_lt(Actual, Expected)`` Assumes Actual is less than Expected +``cr_assume_str_lt(Actual, Expected)`` Assumes Actual is less than Expected lexicographically. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_leq(Actual, Expected)`` Assumes Actual is less or equal to Expected +``cr_assume_str_leq(Actual, Expected)`` Assumes Actual is less or equal to Expected lexicographically. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_gt(Actual, Expected)`` Assumes Actual is greater than Expected +``cr_assume_str_gt(Actual, Expected)`` Assumes Actual is greater than Expected lexicographically. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_strings_geq(Actual, Expected)`` Assumes Actual is greater or equal to Expected +``cr_assume_str_geq(Actual, Expected)`` Assumes Actual is greater or equal to Expected lexicographically. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_arrays_eq(Actual, Expected, Size)`` Assumes all elements of Actual (from 0 to Size - 1) +``cr_assume_arr_eq(Actual, Expected, Size)`` Assumes all elements of Actual (from 0 to Size - 1) are equals to those of Expected. ------------------------------------------------------- ---------------------------------------------------- -``cr_assume_arrays_neq(Actual, Unexpected, Size)`` Assumes one or more elements of Actual (from 0 to +``cr_assume_arr_neq(Actual, Unexpected, Size)`` Assumes one or more elements of Actual (from 0 to Size - 1) differs from their counterpart in Expected. ======================================================= ==================================================== diff --git a/include/criterion/abort.h b/include/criterion/abort.h index 026289f2..5d949d7b 100644 --- a/include/criterion/abort.h +++ b/include/criterion/abort.h @@ -24,13 +24,26 @@ #ifndef CRITERION_ABORT_H_ # define CRITERION_ABORT_H_ -# include "common.h" +# include "internal/common.h" CR_BEGIN_C_API -CR_API NORETURN void criterion_abort_test(void); +/** + * Aborts the current test, marking it as failed. + * + * This function does not return. + */ +CR_API CR_NORETURN void criterion_abort_test(void); + +/** + * Continues the current test. + * + * Used as a counterpart to criterion_abort_test. + */ CR_INLINE static void criterion_continue_test(void) {} +CR_API void criterion_test_die(const char *msg, ...); + CR_END_C_API #endif /* !CRITERION_ABORT_H_ */ diff --git a/include/criterion/alloc.h b/include/criterion/alloc.h index 8b80eadb..555a1dec 100644 --- a/include/criterion/alloc.h +++ b/include/criterion/alloc.h @@ -24,14 +24,66 @@ #ifndef CRITERION_ALLOC_H_ # define CRITERION_ALLOC_H_ -# include -# include "common.h" +# ifdef __cplusplus +# include +# include +using std::size_t; +# else +# include +# endif +# include "internal/common.h" CR_BEGIN_C_API +/** + * Allocates a block of memory usable by the test. + * + * It is undefined behaviour to access a pointer returned by malloc(3) + * inside a test or its setup and teardown functions; cr_malloc must + * be use in its place for this purpose. + * + * This function is semantically identical to malloc(3). + * + * @param[in] size The minimal size in bytes of the newly allocated memory. + * @returns The pointer to the start of the allocated memory. + */ CR_API void *cr_malloc(size_t size); + +/** + * Allocates and zero-initialize a block of memory usable by the test. + * + * It is undefined behaviour to access a pointer returned by calloc(3) + * inside a test or its setup and teardown functions; cr_calloc must + * be use in its place for this purpose. + * + * This function is semantically identical to calloc(3). + * + * @param[in] nmemb The number of elements to allocate + * @param[in] size The minimal size of each element. + * @returns The pointer to the start of the allocated memory. + */ CR_API void *cr_calloc(size_t nmemb, size_t size); + +/** + * Reallocates a block of memory usable by the test. + * + * It is undefined behaviour to access a pointer returned by realloc(3) + * inside a test or its setup and teardown functions; cr_realloc must + * be used in its place for this purpose. + * + * This function is semantically identical to realloc(3). + * + * @param[in] ptr A pointer to the memory that needs to be resized. + * @param[in] size The minimal size of the reallocated memory. + * @returns The pointer to the start of the reallocated memory. + */ CR_API void *cr_realloc(void *ptr, size_t size); + +/** + * Free a block of memory allocated by cr_malloc, cr_free or cr_realloc. + * + * @param[in] ptr A pointer to the memory that needs to be freed. + */ CR_API void cr_free(void *ptr); CR_END_C_API @@ -46,6 +98,19 @@ namespace criterion { void *(*const calloc)(size_t, size_t) = cr_calloc; void *(*const realloc)(void *, size_t) = cr_realloc; + /** + * Allocates and construct a new object. + * + * It is undefined behaviour to access a pointer returned by the new + * operator inside a test or its setup and teardown functions; + * new_obj must be used in its place for this purpose. + * + * This function is semantically identical to the new operator. + * + * @tparam T The type of the object to construct + * @param[in] params The constructor parameters of T. + * @returns The pointer to the newly constructed object. + */ template T* new_obj(Params... params) { T* obj = static_cast(cr_malloc(sizeof (T))); @@ -53,6 +118,19 @@ namespace criterion { return obj; } + /** + * Allocates and construct a new array of primitive types + * + * It is undefined behaviour to access a pointer returned by the new[] + * operator inside a test or its setup and teardown functions; + * new_arr must be used in its place for this purpose. + * + * This function is semantically identical to the new[] operator. + * + * @tparam T The compound type of the array to construct + * @param[in] len The length of the array. + * @returns The pointer to the newly constructed array. + */ template typename std::enable_if::value>::type* new_arr(size_t len) { @@ -62,6 +140,19 @@ namespace criterion { return arr; } + /** + * Allocates and construct a new array of object types + * + * It is undefined behaviour to access a pointer returned by the new[] + * operator inside a test or its setup and teardown functions; + * new_arr must be used in its place for this purpose. + * + * This function is semantically identical to the new[] operator. + * + * @tparam T The compound type of the array to construct + * @param[in] len The length of the array. + * @returns The pointer to the newly constructed array. + */ template T* new_arr(size_t len) { void *ptr = cr_malloc(sizeof (size_t) + sizeof (T) * len); @@ -73,27 +164,100 @@ namespace criterion { return arr; } + /** + * Destroys and frees an object allocated by new_obj. + * + * This function is semantically identical to the delete operator. + * + * @tparam T The type of the object to construct + * @param[in] ptr The object to destroy. + */ template void delete_obj(T* ptr) { ptr->~T(); cr_free(ptr); } + /** + * Destroys and frees an array allocated by delete_arr. + * + * This function is semantically identical to the delete[] operator. + * + * @tparam T The type of the object to construct + * @param[in] ptr The object to destroy. + */ template void delete_arr(typename std::enable_if::value>::type* ptr) { cr_free(ptr); } + /** + * Destroys and frees an array allocated by delete_arr. + * + * This function is semantically identical to the delete[] operator. + * + * @tparam T The type of the object to construct + * @param[in] ptr The object to destroy. + */ template void delete_arr(T* ptr) { - size_t len = *(reinterpret_cast(ptr)); - T* arr = reinterpret_cast(reinterpret_cast(ptr) + 1); - for (int i = 0; i < len; ++i) { + size_t *ptr_ = reinterpret_cast(ptr); + size_t len = *(ptr_ - 1); + T* arr = reinterpret_cast(ptr_); + for (size_t i = 0; i < len; ++i) arr[i].~T(); - } - cr_free(ptr); + cr_free(ptr_ - 1); } + /** + * Allocator for use in the STL. + * + * This internally uses calls to the cr_malloc function family, which + * means that STL collections can be safely used inside tests or + * setup/teardown functions if this allocator is used. + */ + template + struct allocator { + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + template + struct rebind { + typedef allocator other; + }; + + inline explicit allocator() {} + inline ~allocator() {} + inline explicit allocator(allocator const&) {} + template + inline explicit allocator(allocator const&) {} + + inline pointer address(reference r) { return &r; } + inline const_pointer address(const_reference r) { return &r; } + + inline pointer allocate(size_type cnt, typename std::allocator::const_pointer = 0) { + return reinterpret_cast(cr_malloc(cnt * sizeof (T))); + } + + inline void deallocate(pointer p, size_type) { cr_free(p); } + + inline size_type max_size() const { + return size_type(-1) / sizeof(T); + } + + inline void construct(pointer p, const T& t) { new(p) T(t); } + inline void construct(pointer p, T&& t) { new (p) T(std::move(t)); } + inline void destroy(pointer p) { p->~T(); } + + inline bool operator==(allocator const&) { return true; } + inline bool operator!=(allocator const& a) { return !operator==(a); } + }; + } # endif diff --git a/include/criterion/assert.h b/include/criterion/assert.h index f29860ed..fee1d4e8 100644 --- a/include/criterion/assert.h +++ b/include/criterion/assert.h @@ -24,209 +24,24 @@ #ifndef CRITERION_ASSERT_H_ # define CRITERION_ASSERT_H_ -# include "preprocess.h" -# include "asprintf-compat.h" - # ifdef __cplusplus -# include -# include # include -# else -# include -# include -# include -# endif -# include "designated-initializer-compat.h" -# include "types.h" -# include "stats.h" -# include "hooks.h" -# include "event.h" -# include "abort.h" - -struct criterion_assert_args { - const char *msg; - int sentinel_; - -#ifdef __cplusplus - constexpr criterion_assert_args(const char *msg) : msg(msg), sentinel_(0) {} - constexpr criterion_assert_args(const char *msg, int sentinel_) : msg(msg), sentinel_(sentinel_) {} -#endif -}; - -// Do NOT reorder unless you want to break the ABI -enum criterion_assert_messages { - CRITERION_ASSERT_MSG_FAIL, - CRITERION_ASSERT_MSG_EXPR_FALSE, - CRITERION_ASSERT_MSG_EXPR_AS_STRINGS_FALSE, - CRITERION_ASSERT_MSG_IS_NULL, - CRITERION_ASSERT_MSG_IS_NOT_NULL, - CRITERION_ASSERT_MSG_IS_EMPTY, - CRITERION_ASSERT_MSG_IS_NOT_EMPTY, - CRITERION_ASSERT_MSG_FILE_STR_MATCH, - CRITERION_ASSERT_MSG_FILE_MATCH, - CRITERION_ASSERT_MSG_THROW, - CRITERION_ASSERT_MSG_NO_THROW, - CRITERION_ASSERT_MSG_ANY_THROW, - CRITERION_ASSERT_MSG_NONE_THROW, -}; - -CR_BEGIN_C_API - -CR_API char *translate_assert_msg(int msg_index, ...); - -CR_END_C_API - -# define CR_GET_CONDITION(Condition, ...) Condition -# define CR_GET_CONDITION_STR(Condition, ...) #Condition -# define CR_VA_SKIP(_, ...) __VA_ARGS__ - -# ifdef __cplusplus -# define CR_STDN std:: -# else -# define CR_STDN -# endif - -# define CR_TRANSLATE_DEF_MSG__(Arg) \ - CR_IDENTITY Arg - -# define CR_TRANSLATE_DEF_MSG_(...) \ - CR_EXPAND(translate_assert_msg( \ - CR_VA_HEAD(__VA_ARGS__), \ - "" CR_TRANSLATE_DEF_MSG__(CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__))) \ - )) - -# define CR_INIT_STATS_(BufSize, MsgVar, ...) CR_EXPAND( \ - do { \ - char *def_msg = CR_EXPAND(CR_TRANSLATE_DEF_MSG_(__VA_ARGS__)); \ - char *formatted_msg = NULL; \ - int msglen = cr_asprintf(&formatted_msg, \ - "" CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))); \ - if (formatted_msg && *formatted_msg) { \ - MsgVar = formatted_msg; \ - CR_STDN free(def_msg); \ - } else { \ - MsgVar = def_msg; \ - msglen = strlen(def_msg); \ - CR_STDN free(formatted_msg); \ - } \ - \ - BufSize = sizeof(struct criterion_assert_stats) \ - + sizeof (size_t) + msglen + 1; \ - \ - char *buf = (char*) CR_STDN malloc(BufSize); \ - stat = (struct criterion_assert_stats*) buf; \ - CR_STDN memset(buf, 0, sizeof (struct criterion_assert_stats)); \ - buf += sizeof (struct criterion_assert_stats); \ - *((size_t*) buf) = msglen + 1; \ - buf += sizeof (size_t); \ - CR_STDN strcpy(buf, MsgVar); \ - CR_STDN free(MsgVar); \ - } while (0)) - -# define CR_FAIL_ABORT_ criterion_abort_test -# define CR_FAIL_CONTINUES_ criterion_continue_test - -# ifdef __GNUC__ -// We disable the format-zero-length warning because we use the validity of -// asprintf(out, "") for empty assertion messages -# pragma GCC diagnostic ignored "-Wformat-zero-length" # endif -# define cr_assert_impl(Fail, Condition, ...) \ - do { \ - bool passed = !!(Condition); \ - \ - char *msg = NULL; \ - size_t bufsize; \ - \ - struct criterion_assert_stats *stat; \ - CR_EXPAND(CR_INIT_STATS_(bufsize, msg, CR_VA_TAIL(__VA_ARGS__))); \ - stat->passed = passed; \ - stat->file = __FILE__; \ - stat->line = __LINE__; \ - \ - send_event(ASSERT, stat, bufsize); \ - CR_STDN free(stat); \ - \ - if (!passed) \ - Fail(); \ - } while (0) +# include "internal/assert.h" // Base assertions -# define cr_fail(Fail, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - 0, \ - dummy, \ - CRITERION_ASSERT_MSG_FAIL, \ - (), \ - __VA_ARGS__ \ - )) - # define cr_assert_fail(...) CR_EXPAND(cr_fail(CR_FAIL_ABORT_, __VA_ARGS__)) # define cr_expect_fail(...) CR_EXPAND(cr_fail(CR_FAIL_CONTINUES_, __VA_ARGS__)) -# define cr_assert(...) \ - CR_EXPAND(cr_assert_impl( \ - CR_FAIL_ABORT_, \ - CR_VA_HEAD(__VA_ARGS__), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR(CR_VA_HEAD(__VA_ARGS__))), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - -# define cr_expect(...) \ - CR_EXPAND(cr_assert_impl( \ - CR_FAIL_CONTINUES_, \ - CR_VA_HEAD(__VA_ARGS__), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR(CR_VA_HEAD(__VA_ARGS__))), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - -# define cr_assert_not(...) \ - CR_EXPAND(cr_assert_impl( \ - CR_FAIL_ABORT_, \ - !(CR_VA_HEAD(__VA_ARGS__)), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR(!(CR_VA_HEAD(__VA_ARGS__)))), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - -# define cr_expect_not(...) \ - CR_EXPAND(cr_assert_impl( \ - CR_FAIL_CONTINUES_, \ - !(CR_VA_HEAD(__VA_ARGS__)), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR(!(CR_VA_HEAD(__VA_ARGS__)))), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) +# define cr_assert(...) CR_EXPAND(cr_assert_(__VA_ARGS__)) +# define cr_expect(...) CR_EXPAND(cr_expect_(__VA_ARGS__)) -// Common binary assertions +# define cr_assert_not(...) CR_EXPAND(cr_assert_not_(__VA_ARGS__)) +# define cr_expect_not(...) CR_EXPAND(cr_expect_not_(__VA_ARGS__)) -# define cr_assert_op_(Fail, Op, Actual, Expected, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - (Actual) Op (Expected), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR((Actual) Op (Expected))), \ - __VA_ARGS__ \ - )) - -# define cr_assert_op_va_(Fail, Op, ...) \ - CR_EXPAND(cr_assert_op_( \ - Fail, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) +// Common binary assertions # define cr_assert_eq(...) CR_EXPAND(cr_assert_op_va_(CR_FAIL_ABORT_, ==, __VA_ARGS__)) # define cr_expect_eq(...) CR_EXPAND(cr_assert_op_va_(CR_FAIL_CONTINUES_, ==, __VA_ARGS__)) @@ -248,25 +63,6 @@ CR_END_C_API // Common unary assertions -# define cr_assert_null_op_(Fail, Op, Msg, Value, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - (Value) Op NULL, \ - dummy, \ - Msg, \ - (CR_STR(Value)), \ - __VA_ARGS__ \ - )) - -# define cr_assert_null_op_va_(Fail, Op, Msg, ...) \ - CR_EXPAND(cr_assert_null_op_( \ - Fail, \ - Op, \ - Msg, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - # define cr_assert_null(...) CR_EXPAND(cr_assert_null_op_va_(CR_FAIL_ABORT_, ==, CRITERION_ASSERT_MSG_IS_NOT_NULL, __VA_ARGS__)) # define cr_expect_null(...) CR_EXPAND(cr_assert_null_op_va_(CR_FAIL_CONTINUES_, ==, CRITERION_ASSERT_MSG_IS_NOT_NULL, __VA_ARGS__)) @@ -275,32 +71,6 @@ CR_END_C_API // Floating-point assertions -# define cr_assert_float_eq_op_(Actual, Expected, Epsilon) \ - (Expected) - (Actual) <= (Epsilon) && (Actual) - (Expected) <= (Epsilon) - -# define cr_assert_float_neq_op_(Actual, Expected, Epsilon) \ - (Expected) - (Actual) > (Epsilon) || (Actual) - (Expected) > (Epsilon) - -# define cr_assert_float_op_(Fail, Op, Actual, Expected, Epsilon, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - Op(Actual, Expected, Epsilon), \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR(Op(Actual, Expected, Epsilon))), \ - __VA_ARGS__ \ - )) - -# define cr_assert_float_op_va_(Fail, Op, ...) \ - CR_EXPAND(cr_assert_float_op_( \ - Fail, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ - CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ - )) - # define cr_assert_float_eq(...) CR_EXPAND(cr_assert_float_op_va_(CR_FAIL_ABORT_, cr_assert_float_eq_op_, __VA_ARGS__)) # define cr_expect_float_eq(...) CR_EXPAND(cr_assert_float_op_va_(CR_FAIL_CONTINUES_, cr_assert_float_eq_op_, __VA_ARGS__)) @@ -309,50 +79,12 @@ CR_END_C_API // String assertions -# define cr_assert_str_op_empty_(Fail, Op, Msg, Value, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - (Value)[0] Op '\0', \ - dummy, \ - Msg, \ - (CR_STR(Value)), \ - __VA_ARGS__ \ - )) - -# define cr_assert_str_op_empty_va_(Fail, Op, Msg, ...) \ - CR_EXPAND(cr_assert_str_op_empty_( \ - Fail, \ - Op, \ - Msg, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - # define cr_assert_str_empty(...) CR_EXPAND(cr_assert_str_op_empty_va_(CR_FAIL_ABORT_, ==, CRITERION_ASSERT_MSG_IS_NOT_EMPTY, __VA_ARGS__)) # define cr_expect_str_empty(...) CR_EXPAND(cr_assert_str_op_empty_va_(CR_FAIL_CONTINUES_, ==, CRITERION_ASSERT_MSG_IS_NOT_EMPTY, __VA_ARGS__)) # define cr_assert_str_not_empty(...) CR_EXPAND(cr_assert_str_op_empty_va_(CR_FAIL_ABORT_, !=, CRITERION_ASSERT_MSG_IS_EMPTY, __VA_ARGS__)) # define cr_expect_str_not_empty(...) CR_EXPAND(cr_assert_str_op_empty_va_(CR_FAIL_CONTINUES_, !=, CRITERION_ASSERT_MSG_IS_EMPTY, __VA_ARGS__)) -# define cr_assert_str_op_(Fail, Op, Actual, Expected, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - CR_STDN strcmp((Actual), (Expected)) Op 0, \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_AS_STRINGS_FALSE, \ - (CR_STR((Actual) Op (Expected))), \ - __VA_ARGS__ \ - )) - -# define cr_assert_str_op_va_(Fail, Op, ...) \ - CR_EXPAND(cr_assert_str_op_( \ - Fail, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) - # define cr_assert_str_eq(...) CR_EXPAND(cr_assert_str_op_va_(CR_FAIL_ABORT_, ==, __VA_ARGS__)) # define cr_expect_str_eq(...) CR_EXPAND(cr_assert_str_op_va_(CR_FAIL_CONTINUES_, ==, __VA_ARGS__)) @@ -373,26 +105,6 @@ CR_END_C_API // Array assertions -# define cr_assert_mem_op_(Fail, Op, Actual, Expected, Size, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - CR_STDN memcmp((Actual), (Expected), (Size)) Op 0, \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR((Actual)[0 .. Size] Op (Expected)[0 .. Size])), \ - __VA_ARGS__ \ - )) - -# define cr_assert_mem_op_va_(Fail, Op, ...) \ - CR_EXPAND(cr_assert_mem_op_( \ - Fail, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ - CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ - )) - # define cr_assert_arr_eq(...) CR_EXPAND(cr_assert_mem_op_va_(CR_FAIL_ABORT_, ==, __VA_ARGS__)) # define cr_expect_arr_eq(...) CR_EXPAND(cr_assert_mem_op_va_(CR_FAIL_CONTINUES_, ==, __VA_ARGS__)) @@ -401,44 +113,7 @@ CR_END_C_API // Safe array comparison assertions -# if defined(__GNUC__) || defined(__cplusplus) - -# ifdef __cplusplus -# define CR_ARR_COMPARE_(A, B, Size, Cmp, Result) \ - int Result = std::lexicographical_compare((A), (A) + Size, (B), (B) + Size, Cmp) -# else -# define CR_ARR_COMPARE_(A, B, Size, Cmp, Result) \ - __typeof__(&(A)[0]) first = (A); \ - __typeof__(&(B)[0]) second = (B); \ - int Result = 0; \ - size_t i, size; \ - for (i = 0, size = (Size); !Result && i < size; ++i) \ - Result = Cmp(first + i, second + i) -# endif - -# define cr_assert_arr_op_cmp_(Fail, Op, Actual, Expected, Size, Cmp, ...) \ - do { \ - CR_ARR_COMPARE_(Actual, Expected, Size, Cmp, order); \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - order Op 0, \ - dummy, \ - CRITERION_ASSERT_MSG_EXPR_FALSE, \ - (CR_STR((Actual)[0 .. Size] Op (Expected)[0 .. Size])), \ - __VA_ARGS__ \ - )); \ - } while (0) - -# define cr_assert_arr_op_cmp_va_(Fail, Op, ...) \ - CR_EXPAND(cr_assert_arr_op_cmp_( \ - Fail, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)))), \ - CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)))) \ - )) +# if defined(__GNUC__) || defined(__clang__) || defined(__cplusplus) # define cr_assert_arr_eq_cmp(...) CR_EXPAND(cr_assert_arr_op_cmp_va_(CR_FAIL_ABORT_, ==, __VA_ARGS__)) # define cr_expect_arr_eq_cmp(...) CR_EXPAND(cr_assert_arr_op_cmp_va_(CR_FAIL_CONTINUES_, ==, __VA_ARGS__)) @@ -460,14 +135,6 @@ CR_END_C_API # else -# define CRITERION_GNUC_WARN__(Msg) \ - _Pragma(#Msg) - -# define CRITERION_GNUC_WARN_(Name) CRITERION_GNUC_WARN__( \ - message \ - "The `" #Name "` macro is only available on GNU C compilers." \ - ) - # define cr_assert_arr_eq_cmp(...) CRITERION_GNUC_WARN_(cr_assert_arr_eq_cmp) CR_NOOP # define cr_expect_arr_eq_cmp(...) CRITERION_GNUC_WARN_(cr_expect_arr_eq_cmp) CR_NOOP @@ -490,103 +157,15 @@ CR_END_C_API # ifdef __cplusplus -# define cr_assert_throw_abort_(Fail, Msg, MsgArgs, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - 0, \ - dummy, \ - Msg, \ - MsgArgs, \ - CR_VA_TAIL(__VA_ARGS__) \ - )) - -# define cr_assert_throw_(Fail, Statement, Exception, ...) \ - try { \ - Statement; \ - } catch (Exception const &) { \ - } catch (...) { \ - CR_EXPAND(cr_assert_throw_abort_( \ - Fail, \ - CRITERION_ASSERT_MSG_NO_THROW, \ - (CR_STR(Statement), CR_STR(Exception)), \ - __VA_ARGS__)); \ - } - -# define cr_assert_throw_va_(...) \ - CR_EXPAND(cr_assert_throw_( \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ - dummy, \ - CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ - )) - # define cr_assert_throw(...) CR_EXPAND(cr_assert_throw_va_(CR_FAIL_ABORT_, __VA_ARGS__)) # define cr_expect_throw(...) CR_EXPAND(cr_assert_throw_va_(CR_FAIL_CONTINUES_, __VA_ARGS__)) -# define cr_assert_no_throw_(Fail, Statement, Exception, ...) \ - try { \ - Statement; \ - } catch (Exception const &) { \ - CR_EXPAND(cr_assert_throw_abort_( \ - Fail, \ - CRITERION_ASSERT_MSG_THROW, \ - (CR_STR(Statement), CR_STR(Exception)), \ - __VA_ARGS__)); \ - } - -# define cr_assert_no_throw_va_(...) \ - CR_EXPAND(cr_assert_no_throw_( \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ - dummy, \ - CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ - )) - # define cr_assert_no_throw(...) CR_EXPAND(cr_assert_no_throw_va_(CR_FAIL_ABORT_, __VA_ARGS__)) # define cr_expect_no_throw(...) CR_EXPAND(cr_assert_no_throw_va_(CR_FAIL_CONTINUES_, __VA_ARGS__)) -# define cr_assert_any_throw_(Fail, Statement, ...) \ - try { \ - Statement; \ - CR_EXPAND(cr_assert_throw_abort_( \ - Fail, \ - CRITERION_ASSERT_MSG_ANY_THROW, \ - (CR_STR(Statement)), \ - __VA_ARGS__)); \ - } catch (...) {} - -# define cr_assert_any_throw_va_(...) \ - CR_EXPAND(cr_assert_any_throw_( \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - dummy, \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) - # define cr_assert_any_throw(...) CR_EXPAND(cr_assert_any_throw_va_(CR_FAIL_ABORT_, __VA_ARGS__)) # define cr_expect_any_throw(...) CR_EXPAND(cr_assert_any_throw_va_(CR_FAIL_CONTINUES_, __VA_ARGS__)) -# define cr_assert_none_throw_(Fail, Statement, ...) \ - try { \ - Statement; \ - } catch (...) { \ - CR_EXPAND(cr_assert_throw_abort_( \ - Fail, \ - CRITERION_ASSERT_MSG_NONE_THROW, \ - (CR_STR(Statement)), \ - __VA_ARGS__)); \ - } - -# define cr_assert_none_throw_va_(...) \ - CR_EXPAND(cr_assert_none_throw_( \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - dummy, \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) - # define cr_assert_none_throw(...) CR_EXPAND(cr_assert_none_throw_va_(CR_FAIL_ABORT_, __VA_ARGS__)) # define cr_expect_none_throw(...) CR_EXPAND(cr_assert_none_throw_va_(CR_FAIL_CONTINUES_, __VA_ARGS__)) @@ -596,12 +175,6 @@ CR_END_C_API // It shall be removed in the next major version of Criterion # ifndef CRITERION_NO_COMPAT -# define CRITERION_ASSERT_DEPRECATED_(Name) CRITERION_ASSERT_DEPRECATED__( \ - message \ - ("The `" #Name "` macro is deprecated, " \ - "please use `cr_" #Name "` instead.") \ - ) - # define CRITERION_ASSERT_DEPRECATED_B(Name, Newname) \ CRITERION_ASSERT_DEPRECATED__( \ message \ @@ -617,16 +190,6 @@ CR_END_C_API _Pragma(#Msg) # endif -# ifndef assert -# define assert(...) CRITERION_ASSERT_DEPRECATED_(assert) cr_assert(__VA_ARGS__) - -// this is needed to make the POSIX assert.h redefine assert if -// subsequently included -# ifndef _ASSERT_H -# define _ASSERT_H 1 -# endif /* !_ASSERT_H */ -# endif /* !assert */ - // scheduled for removal after 2.0 # define cr_abort_test(Message) CRITERION_ASSERT_DEPRECATED_B(cr_abort_test, cr_assert_fail) cr_assert_fail(Message) # define cr_assert_strings_eq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assert_strings_eq, cr_assert_str_eq) cr_assert_str_eq(__VA_ARGS__) @@ -642,52 +205,6 @@ CR_END_C_API # define cr_assert_arrays_eq_cmp(...) CRITERION_ASSERT_DEPRECATED_B(cr_assert_arrays_eq_cmp, cr_assert_arr_eq_cmp) cr_assert_arr_eq_cmp(__VA_ARGS__) # define cr_assert_arrays_neq_cmp(...) CRITERION_ASSERT_DEPRECATED_B(cr_assert_arrays_neq_cmp, cr_assert_arr_neq_cmp) cr_assert_arr_neq_cmp(__VA_ARGS__) -// scheduled for removal at 2.0 -# define abort_test(Message) CRITERION_ASSERT_DEPRECATED_(abort_test) cr_abort_test(Message) -# define expect(...) CRITERION_ASSERT_DEPRECATED_(expect) cr_expect(__VA_ARGS__) -# define assert_not(...) CRITERION_ASSERT_DEPRECATED_(assert_not) cr_assert_not(__VA_ARGS__) -# define expect_not(...) CRITERION_ASSERT_DEPRECATED_(expect_not) cr_expect_not(__VA_ARGS__) -# define assert_eq(...) CRITERION_ASSERT_DEPRECATED_(assert_eq) cr_assert_eq(__VA_ARGS__) -# define expect_eq(...) CRITERION_ASSERT_DEPRECATED_(expect_eq) cr_expect_eq(__VA_ARGS__) -# define assert_neq(...) CRITERION_ASSERT_DEPRECATED_(assert_neq) cr_assert_neq(__VA_ARGS__) -# define expect_neq(...) CRITERION_ASSERT_DEPRECATED_(expect_neq) cr_expect_neq(__VA_ARGS__) -# define assert_lt(...) CRITERION_ASSERT_DEPRECATED_(assert_lt) cr_assert_lt(__VA_ARGS__) -# define expect_lt(...) CRITERION_ASSERT_DEPRECATED_(expect_lt) cr_expect_lt(__VA_ARGS__) -# define assert_gt(...) CRITERION_ASSERT_DEPRECATED_(assert_gt) cr_assert_gt(__VA_ARGS__) -# define expect_gt(...) CRITERION_ASSERT_DEPRECATED_(expect_gt) cr_expect_gt(__VA_ARGS__) -# define assert_leq(...) CRITERION_ASSERT_DEPRECATED_(assert_leq) cr_assert_leq(__VA_ARGS__) -# define expect_leq(...) CRITERION_ASSERT_DEPRECATED_(expect_leq) cr_expect_leq(__VA_ARGS__) -# define assert_geq(...) CRITERION_ASSERT_DEPRECATED_(assert_geq) cr_assert_geq(__VA_ARGS__) -# define expect_geq(...) CRITERION_ASSERT_DEPRECATED_(expect_geq) cr_expect_geq(__VA_ARGS__) -# define assert_null(...) CRITERION_ASSERT_DEPRECATED_(assert_null) cr_assert_null(__VA_ARGS__) -# define expect_null(...) CRITERION_ASSERT_DEPRECATED_(expect_null) cr_expect_null(__VA_ARGS__) -# define assert_not_null(...) CRITERION_ASSERT_DEPRECATED_(assert_not_null) cr_assert_not_null(__VA_ARGS__) -# define expect_not_null(...) CRITERION_ASSERT_DEPRECATED_(expect_not_null) cr_expect_not_null(__VA_ARGS__) -# define assert_float_eq(...) CRITERION_ASSERT_DEPRECATED_(assert_float_eq) cr_assert_float_eq(__VA_ARGS__) -# define expect_float_eq(...) CRITERION_ASSERT_DEPRECATED_(expect_float_eq) cr_expect_float_eq(__VA_ARGS__) -# define assert_float_neq(...) CRITERION_ASSERT_DEPRECATED_(assert_float_neq) cr_assert_float_neq(__VA_ARGS__) -# define expect_float_neq(...) CRITERION_ASSERT_DEPRECATED_(expect_float_neq) cr_expect_float_neq(__VA_ARGS__) -# define assert_strings_eq(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_eq) cr_assert_strings_eq(__VA_ARGS__) -# define expect_strings_eq(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_eq) cr_expect_strings_eq(__VA_ARGS__) -# define assert_strings_neq(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_neq) cr_assert_strings_neq(__VA_ARGS__) -# define expect_strings_neq(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_neq) cr_expect_strings_neq(__VA_ARGS__) -# define assert_strings_gt(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_gt) cr_assert_strings_gt(__VA_ARGS__) -# define expect_strings_gt(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_gt) cr_expect_strings_gt(__VA_ARGS__) -# define assert_strings_lt(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_lt) cr_assert_strings_lt(__VA_ARGS__) -# define expect_strings_lt(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_lt) cr_expect_strings_lt(__VA_ARGS__) -# define assert_strings_leq(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_leq) cr_assert_strings_leq(__VA_ARGS__) -# define expect_strings_leq(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_leq) cr_expect_strings_leq(__VA_ARGS__) -# define assert_strings_geq(...) CRITERION_ASSERT_DEPRECATED_(assert_strings_geq) cr_assert_strings_geq(__VA_ARGS__) -# define expect_strings_geq(...) CRITERION_ASSERT_DEPRECATED_(expect_strings_geq) cr_expect_strings_geq(__VA_ARGS__) -# define assert_arrays_eq(...) CRITERION_ASSERT_DEPRECATED_(assert_arrays_eq) cr_assert_arrays_eq(__VA_ARGS__) -# define expect_arrays_eq(...) CRITERION_ASSERT_DEPRECATED_(expect_arrays_eq) cr_expect_arrays_eq(__VA_ARGS__) -# define assert_arrays_neq(...) CRITERION_ASSERT_DEPRECATED_(assert_arrays_neq) cr_assert_arrays_neq(__VA_ARGS__) -# define expect_arrays_neq(...) CRITERION_ASSERT_DEPRECATED_(expect_arrays_neq) cr_expect_arrays_neq(__VA_ARGS__) -# define assert_arrays_eq_cmp(...) CRITERION_ASSERT_DEPRECATED_(assert_arrays_eq_cmp) cr_assert_arrays_eq_cmp(__VA_ARGS__) -# define expect_arrays_eq_cmp(...) CRITERION_ASSERT_DEPRECATED_(expect_arrays_eq_cmp) cr_expect_arrays_eq_cmp(__VA_ARGS__) -# define assert_arrays_neq_cmp(...) CRITERION_ASSERT_DEPRECATED_(assert_arrays_neq_cmp) cr_assert_arrays_neq_cmp(__VA_ARGS__) -# define expect_arrays_neq_cmp(...) CRITERION_ASSERT_DEPRECATED_(expect_arrays_neq_cmp) cr_expect_arrays_neq_cmp(__VA_ARGS__) - # endif #endif /* !CRITERION_ASSERT_H_ */ diff --git a/include/criterion/criterion.h b/include/criterion/criterion.h index db9dfdcb..a1b183bc 100644 --- a/include/criterion/criterion.h +++ b/include/criterion/criterion.h @@ -24,74 +24,98 @@ #ifndef CRITERION_H_ # define CRITERION_H_ -# include "designated-initializer-compat.h" -# include "common.h" # include "types.h" # include "assert.h" # include "alloc.h" -# define IDENTIFIER_(Category, Name, Suffix) \ - Category ## _ ## Name ## _ ## Suffix +# include "internal/test.h" -# ifdef __cplusplus -# define TEST_PROTOTYPE_(Category, Name) \ - extern "C" void IDENTIFIER_(Category, Name, impl)(void) -# else -# define TEST_PROTOTYPE_(Category, Name) \ - void IDENTIFIER_(Category, Name, impl)(void) -# endif - -# define SUITE_IDENTIFIER_(Name, Suffix) \ - suite_ ## Name ## _ ## Suffix - -# define Test(...) CR_EXPAND(Test_(__VA_ARGS__, .sentinel_ = 0)) -# define Test_(Category, Name, ...) \ - TEST_PROTOTYPE_(Category, Name); \ - struct criterion_test_extra_data IDENTIFIER_(Category, Name, extra) = \ - CR_EXPAND(CRITERION_MAKE_STRUCT(struct criterion_test_extra_data, \ - .kind_ = CR_TEST_NORMAL, \ - .param_ = (struct criterion_test_params(*)(void)) NULL, \ - .identifier_ = #Category "/" #Name, \ - .file_ = __FILE__, \ - .line_ = __LINE__, \ - __VA_ARGS__ \ - )); \ - struct criterion_test IDENTIFIER_(Category, Name, meta) = { \ - #Name, \ - #Category, \ - IDENTIFIER_(Category, Name, impl), \ - &IDENTIFIER_(Category, Name, extra) \ - }; \ - SECTION_("cr_tst") \ - struct criterion_test *IDENTIFIER_(Category, Name, ptr) \ - = &IDENTIFIER_(Category, Name, meta) SECTION_SUFFIX_; \ - TEST_PROTOTYPE_(Category, Name) +/** + * Test(Suite, Name, [Options...]) { Function body } + * + * Defines a new test. + * + * @param Suite The name of the test suite containing this test. + * @param Name The name of the test. + * @param Options An optional sequence of designated initializer key/value + * pairs as described in the `criterion_test_extra_data` structure + * (see criterion/types.h). + * Example: .exit_code = 1 + */ +# define Test(...) CR_EXPAND(CR_TEST_BASE(__VA_ARGS__, .sentinel_ = 0)) -# define TestSuite(...) CR_EXPAND(TestSuite_(__VA_ARGS__, .sentinel_ = 0)) -# define TestSuite_(Name, ...) \ - struct criterion_test_extra_data SUITE_IDENTIFIER_(Name, extra) = \ - CR_EXPAND(CRITERION_MAKE_STRUCT(struct criterion_test_extra_data, \ - .file_ = __FILE__, \ - .line_ = 0, \ - __VA_ARGS__ \ - )); \ - struct criterion_suite SUITE_IDENTIFIER_(Name, meta) = { \ - #Name, \ - &SUITE_IDENTIFIER_(Name, extra), \ - }; \ - SECTION_("cr_sts") \ - struct criterion_suite *SUITE_IDENTIFIER_(Name, ptr) \ - = &SUITE_IDENTIFIER_(Name, meta) SECTION_SUFFIX_ +/** + * TestSuite(Name, [Options...]); + * + * Explicitely defines a test suite and its options. + * + * @param Name The name of the test suite. + * @param Options An optional sequence of designated initializer key/value + * pairs as described in the `criterion_test_extra_data` structure + * (see criterion/types.h). + * These options will provide the defaults for each test. + */ +# define TestSuite(...) CR_EXPAND(CR_SUITE_BASE(__VA_ARGS__, .sentinel_ = 0)) CR_BEGIN_C_API +/** + * Initializes criterion and builds a set of all discovered tests. + * + * Using any of the functions and macros provided by criterion before calling + * this results in undefined behaviour. + * + * @returns the set of tests + */ CR_API struct criterion_test_set *criterion_initialize(void); + +/** + * Release all resources allocated by criterion. + * + * Using any of the functions and macros provided by criterion except + * criterion_initialize after this function is called results in undefined + * behaviour. + */ CR_API void criterion_finalize(struct criterion_test_set *tests); + +/** + * Run all the tests in the test set. + * + * @param[in] tests The set of tests that are to be executed. + * + * @returns 1 if all tests succeeded or criterion_options.always_succeed + * is true, 0 otherwise. + */ CR_API int criterion_run_all_tests(struct criterion_test_set *tests); + +/** + * Handles all default command-line parameters, as documented in: + * , and appropriately + * sets criterion_options. + * + * @param[in] argc The number of arguments in argv. + * @param[in] argv A null-terminated array of strings representing the arguments. + * @param[in] handle_unknown_arg Whether the function should print a message + * and exit when an unknown parameter is encountered. Use false if you want + * to handle additional parameters yourself. + * + * @returns 0 if the process should exit immediately after, for instance after + * printing the help message. + */ CR_API int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg); + +/** + * Manually registers a new test within the specified test set. + * + * @param[in] tests The set of tests you want to insert the test in. + * @param[in] test The newly created test. + */ CR_API void criterion_register_test(struct criterion_test_set *tests, struct criterion_test *test); +extern const struct criterion_test *const criterion_current_test; +extern const struct criterion_suite *const criterion_current_suite; + CR_END_C_API #endif /* !CRITERION_H_ */ diff --git a/include/criterion/event.h b/include/criterion/event.h index 51389018..6d2d3a01 100644 --- a/include/criterion/event.h +++ b/include/criterion/event.h @@ -29,11 +29,11 @@ # else # include # endif -# include "common.h" +# include "internal/common.h" CR_BEGIN_C_API -CR_API void send_event(int kind, void *data, size_t size); +CR_API void criterion_send_event(int kind, void *data, size_t size); CR_END_C_API diff --git a/include/criterion/hooks.h b/include/criterion/hooks.h index 62ab4834..d4e34ee3 100644 --- a/include/criterion/hooks.h +++ b/include/criterion/hooks.h @@ -24,9 +24,11 @@ #ifndef CRITERION_HOOKS_H_ # define CRITERION_HOOKS_H_ -# include "common.h" -# include "types.h" +# include "internal/hooks.h" +/** + * This enum lists all the phases of the runner lifecycle. + */ typedef enum { PRE_ALL, PRE_SUITE, @@ -43,57 +45,24 @@ typedef enum { typedef void (*f_report_hook)(); -# define HOOK_IDENTIFIER_(Suffix) HOOK_IDENTIFIER__(__LINE__, Suffix) -# define HOOK_IDENTIFIER__(Line, Suffix) HOOK_IDENTIFIER___(Line, Suffix) -# define HOOK_IDENTIFIER___(Line, Suffix) hook_l ## Line ## _ ## Suffix - -# ifdef __cplusplus -# define HOOK_PROTOTYPE_ \ - extern "C" void HOOK_IDENTIFIER_(impl) -# else -# define HOOK_PROTOTYPE_ \ - void HOOK_IDENTIFIER_(impl) -# endif - -// Section abbreviations -# define HOOK_SECTION_PRE_ALL cr_pra -# define HOOK_SECTION_PRE_SUITE cr_prs -# define HOOK_SECTION_PRE_INIT cr_pri -# define HOOK_SECTION_PRE_TEST cr_prt -# define HOOK_SECTION_ASSERT cr_ast -# define HOOK_SECTION_THEORY_FAIL cr_thf -# define HOOK_SECTION_TEST_CRASH cr_tsc -# define HOOK_SECTION_POST_TEST cr_pot -# define HOOK_SECTION_POST_FINI cr_pof -# define HOOK_SECTION_POST_SUITE cr_pos -# define HOOK_SECTION_POST_ALL cr_poa - -# define HOOK_SECTION(Kind) HOOK_SECTION_ ## Kind - -# define HOOK_SECTION_STRINGIFY__(Sec) #Sec -# define HOOK_SECTION_STRINGIFY_(Sec) HOOK_SECTION_STRINGIFY__(Sec) -# define HOOK_SECTION_STRINGIFY(Kind) HOOK_SECTION_STRINGIFY_(HOOK_SECTION(Kind)) - -# define HOOK_PARAM_TYPE_PRE_ALL struct criterion_test_set * -# define HOOK_PARAM_TYPE_PRE_SUITE struct criterion_suite_set * -# define HOOK_PARAM_TYPE_PRE_INIT struct criterion_test * -# define HOOK_PARAM_TYPE_PRE_TEST struct criterion_test * -# define HOOK_PARAM_TYPE_ASSERT struct criterion_assert_stats * -# define HOOK_PARAM_TYPE_THEORY_FAIL struct criterion_theory_stats * -# define HOOK_PARAM_TYPE_TEST_CRASH struct criterion_test_stats * -# define HOOK_PARAM_TYPE_POST_TEST struct criterion_test_stats * -# define HOOK_PARAM_TYPE_POST_FINI struct criterion_test_stats * -# define HOOK_PARAM_TYPE_POST_SUITE struct criterion_suite_stats * -# define HOOK_PARAM_TYPE_POST_ALL struct criterion_global_stats * - -# define HOOK_PARAM_TYPE(Kind) HOOK_PARAM_TYPE_ ## Kind - -# define ReportHook(Kind) \ - HOOK_PROTOTYPE_(HOOK_PARAM_TYPE(Kind)); \ - SECTION_(HOOK_SECTION_STRINGIFY(Kind)) \ - f_report_hook HOOK_IDENTIFIER_(func) = \ - (f_report_hook) HOOK_IDENTIFIER_(impl) \ - SECTION_SUFFIX_; \ - HOOK_PROTOTYPE_ +/** + * ReportHook(Kind)(Type *param) { Function Body } + * + * Defines a report hook for the phase defined by Kind. + * + * The type of the parameter depends on the phase: + * + * - struct criterion_test_set for PRE_ALL. + * - struct criterion_suite_set for PRE_SUITE. + * - struct criterion_test for PRE_INIT and PRE_TEST. + * - struct criterion_assert_stats for ASSERT. + * - struct criterion_theory_stats for THEORY_FAIL. + * - struct criterion_test_stats for POST_TEST, POST_FINI, and TEST_CRASH. + * - struct criterion_suite_stats for POST_SUITE. + * - struct criterion_global_stats for POST_ALL. + * + * @param Kind The report phase to hook the function onto. + */ +# define ReportHook(Kind) CR_REPORT_HOOK_IMPL(Kind) #endif /* !CRITERION_HOOKS_H_ */ diff --git a/include/criterion/asprintf-compat.h b/include/criterion/internal/asprintf-compat.h similarity index 98% rename from include/criterion/asprintf-compat.h rename to include/criterion/internal/asprintf-compat.h index 6f78efdd..e0316fe8 100644 --- a/include/criterion/asprintf-compat.h +++ b/include/criterion/internal/asprintf-compat.h @@ -34,7 +34,7 @@ CR_BEGIN_C_API -FORMAT(printf, 2, 3) +CR_FORMAT(printf, 2, 3) CR_API int cr_asprintf(char **strp, const char *fmt, ...); CR_API int cr_vasprintf(char **strp, const char *fmt, va_list ap); diff --git a/include/criterion/internal/assert.h b/include/criterion/internal/assert.h new file mode 100644 index 00000000..fd79d793 --- /dev/null +++ b/include/criterion/internal/assert.h @@ -0,0 +1,477 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_ASSERT_H_ +# define CRITERION_INTERNAL_ASSERT_H_ + +# include "common.h" +# include "preprocess.h" +# include "asprintf-compat.h" +# include "designated-initializer-compat.h" + +# ifdef __cplusplus +# include +# include +# else +# include +# include +# include +# endif +# include "../types.h" +# include "../stats.h" +# include "../hooks.h" +# include "../event.h" +# include "../abort.h" + +struct criterion_assert_args { + const char *msg; + int sentinel_; + +#ifdef __cplusplus + constexpr criterion_assert_args(const char *msg) : msg(msg), sentinel_(0) {} + constexpr criterion_assert_args(const char *msg, int sentinel_) : msg(msg), sentinel_(sentinel_) {} +#endif +}; + +// Do NOT reorder unless you want to break the ABI +enum criterion_assert_messages { + CRITERION_ASSERT_MSG_FAIL, + CRITERION_ASSERT_MSG_EXPR_FALSE, + CRITERION_ASSERT_MSG_EXPR_AS_STRINGS_FALSE, + CRITERION_ASSERT_MSG_IS_NULL, + CRITERION_ASSERT_MSG_IS_NOT_NULL, + CRITERION_ASSERT_MSG_IS_EMPTY, + CRITERION_ASSERT_MSG_IS_NOT_EMPTY, + CRITERION_ASSERT_MSG_FILE_STR_MATCH, + CRITERION_ASSERT_MSG_FILE_MATCH, + CRITERION_ASSERT_MSG_THROW, + CRITERION_ASSERT_MSG_NO_THROW, + CRITERION_ASSERT_MSG_ANY_THROW, + CRITERION_ASSERT_MSG_NONE_THROW, +}; + +CR_BEGIN_C_API + +CR_API char *cr_translate_assert_msg(int msg_index, ...); + +CR_END_C_API + +# define CR_GET_CONDITION(Condition, ...) Condition +# define CR_GET_CONDITION_STR(Condition, ...) #Condition +# define CR_VA_SKIP(_, ...) __VA_ARGS__ + +# ifdef __cplusplus +# define CR_STDN std:: +# else +# define CR_STDN +# endif + +# define CR_TRANSLATE_DEF_MSG__(Arg) \ + CR_IDENTITY Arg + +# define CR_TRANSLATE_DEF_MSG_(...) \ + CR_EXPAND(cr_translate_assert_msg( \ + CR_VA_HEAD(__VA_ARGS__), \ + "" CR_TRANSLATE_DEF_MSG__(CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__))) \ + )) + +# define CR_INIT_STATS_(BufSize, MsgVar, ...) CR_EXPAND( \ + do { \ + char *def_msg = CR_EXPAND(CR_TRANSLATE_DEF_MSG_(__VA_ARGS__)); \ + char *formatted_msg = NULL; \ + int msglen = cr_asprintf(&formatted_msg, \ + "" CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))); \ + if (formatted_msg && *formatted_msg) { \ + MsgVar = formatted_msg; \ + CR_STDN free(def_msg); \ + } else { \ + MsgVar = def_msg; \ + msglen = strlen(def_msg); \ + CR_STDN free(formatted_msg); \ + } \ + \ + BufSize = sizeof(struct criterion_assert_stats) \ + + sizeof (size_t) + msglen + 1; \ + \ + char *buf = (char*) CR_STDN malloc(BufSize); \ + stat = (struct criterion_assert_stats*) buf; \ + CR_STDN memset(buf, 0, sizeof (struct criterion_assert_stats)); \ + buf += sizeof (struct criterion_assert_stats); \ + *((size_t*) buf) = msglen + 1; \ + buf += sizeof (size_t); \ + CR_STDN strcpy(buf, MsgVar); \ + CR_STDN free(MsgVar); \ + } while (0)) + +# define CR_FAIL_ABORT_ criterion_abort_test +# define CR_FAIL_CONTINUES_ criterion_continue_test + +# if defined(__GNUC__) || defined(__clang__) +// We disable the format-zero-length warning because we use the validity of +// asprintf(out, "") for empty assertion messages +# pragma GCC diagnostic ignored "-Wformat-zero-length" +# endif + +# define cr_assert_impl(Fail, Condition, ...) \ + do { \ + bool passed = !!(Condition); \ + \ + char *msg = NULL; \ + size_t bufsize; \ + \ + struct criterion_assert_stats *stat; \ + CR_EXPAND(CR_INIT_STATS_(bufsize, msg, CR_VA_TAIL(__VA_ARGS__))); \ + stat->passed = passed; \ + stat->file = __FILE__; \ + stat->line = __LINE__; \ + \ + criterion_send_event(ASSERT, stat, bufsize); \ + CR_STDN free(stat); \ + \ + if (!passed) \ + Fail(); \ + } while (0) + +# define cr_fail(Fail, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + 0, \ + dummy, \ + CRITERION_ASSERT_MSG_FAIL, \ + (), \ + __VA_ARGS__ \ + )) + +# define cr_assert_(...) \ + CR_EXPAND(cr_assert_impl( \ + CR_FAIL_ABORT_, \ + CR_VA_HEAD(__VA_ARGS__), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR(CR_VA_HEAD(__VA_ARGS__))), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +# define cr_expect_(...) \ + CR_EXPAND(cr_assert_impl( \ + CR_FAIL_CONTINUES_, \ + CR_VA_HEAD(__VA_ARGS__), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR(CR_VA_HEAD(__VA_ARGS__))), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +# define cr_assert_not_(...) \ + CR_EXPAND(cr_assert_impl( \ + CR_FAIL_ABORT_, \ + !(CR_VA_HEAD(__VA_ARGS__)), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR(!(CR_VA_HEAD(__VA_ARGS__)))), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +# define cr_expect_not_(...) \ + CR_EXPAND(cr_assert_impl( \ + CR_FAIL_CONTINUES_, \ + !(CR_VA_HEAD(__VA_ARGS__)), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR(!(CR_VA_HEAD(__VA_ARGS__)))), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + + +// Binary + +# define cr_assert_op_(Fail, Op, Actual, Expected, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + (Actual) Op (Expected), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR((Actual) Op (Expected))), \ + __VA_ARGS__ \ + )) + +# define cr_assert_op_va_(Fail, Op, ...) \ + CR_EXPAND(cr_assert_op_( \ + Fail, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +// Unary + +# define cr_assert_null_op_(Fail, Op, Msg, Value, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + (Value) Op NULL, \ + dummy, \ + Msg, \ + (CR_STR(Value)), \ + __VA_ARGS__ \ + )) + +# define cr_assert_null_op_va_(Fail, Op, Msg, ...) \ + CR_EXPAND(cr_assert_null_op_( \ + Fail, \ + Op, \ + Msg, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +// Floating point + +# define cr_assert_float_eq_op_(Actual, Expected, Epsilon) \ + (Expected) - (Actual) <= (Epsilon) && (Actual) - (Expected) <= (Epsilon) + +# define cr_assert_float_neq_op_(Actual, Expected, Epsilon) \ + (Expected) - (Actual) > (Epsilon) || (Actual) - (Expected) > (Epsilon) + +# define cr_assert_float_op_(Fail, Op, Actual, Expected, Epsilon, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + Op(Actual, Expected, Epsilon), \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR(Op(Actual, Expected, Epsilon))), \ + __VA_ARGS__ \ + )) + +# define cr_assert_float_op_va_(Fail, Op, ...) \ + CR_EXPAND(cr_assert_float_op_( \ + Fail, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ + CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ + )) + +// String + +# define cr_assert_str_op_empty_(Fail, Op, Msg, Value, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + (Value)[0] Op '\0', \ + dummy, \ + Msg, \ + (CR_STR(Value)), \ + __VA_ARGS__ \ + )) + +# define cr_assert_str_op_empty_va_(Fail, Op, Msg, ...) \ + CR_EXPAND(cr_assert_str_op_empty_( \ + Fail, \ + Op, \ + Msg, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +# define cr_assert_str_op_(Fail, Op, Actual, Expected, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + CR_STDN strcmp((Actual), (Expected)) Op 0, \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_AS_STRINGS_FALSE, \ + (CR_STR((Actual) Op (Expected))), \ + __VA_ARGS__ \ + )) + +# define cr_assert_str_op_va_(Fail, Op, ...) \ + CR_EXPAND(cr_assert_str_op_( \ + Fail, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +// Array + +# define cr_assert_mem_op_(Fail, Op, Actual, Expected, Size, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + CR_STDN memcmp((Actual), (Expected), (Size)) Op 0, \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR((Actual)[0 .. Size] Op (Expected)[0 .. Size])), \ + __VA_ARGS__ \ + )) + +# define cr_assert_mem_op_va_(Fail, Op, ...) \ + CR_EXPAND(cr_assert_mem_op_( \ + Fail, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ + CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ + )) + +// Array comparisons + +# ifdef __cplusplus +# define CR_ARR_COMPARE_(A, B, Size, Cmp, Result) \ + int Result = std::lexicographical_compare((A), (A) + Size, (B), (B) + Size, Cmp) +# else +# define CR_ARR_COMPARE_(A, B, Size, Cmp, Result) \ + __typeof__(&(A)[0]) first = (A); \ + __typeof__(&(B)[0]) second = (B); \ + int Result = 0; \ + size_t i, size; \ + for (i = 0, size = (Size); !Result && i < size; ++i) \ + Result = Cmp(first + i, second + i) +# endif + +# define cr_assert_arr_op_cmp_(Fail, Op, Actual, Expected, Size, Cmp, ...) \ + do { \ + CR_ARR_COMPARE_(Actual, Expected, Size, Cmp, order); \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + order Op 0, \ + dummy, \ + CRITERION_ASSERT_MSG_EXPR_FALSE, \ + (CR_STR((Actual)[0 .. Size] Op (Expected)[0 .. Size])), \ + __VA_ARGS__ \ + )); \ + } while (0) + +# define cr_assert_arr_op_cmp_va_(Fail, Op, ...) \ + CR_EXPAND(cr_assert_arr_op_cmp_( \ + Fail, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)))), \ + CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)))) \ + )) + +// Exceptions + +# define cr_assert_throw_abort_(Fail, Msg, MsgArgs, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + 0, \ + dummy, \ + Msg, \ + MsgArgs, \ + CR_VA_TAIL(__VA_ARGS__) \ + )) + +# define cr_assert_throw_(Fail, Statement, Exception, ...) \ + try { \ + Statement; \ + } catch (Exception const &) { \ + } catch (...) { \ + CR_EXPAND(cr_assert_throw_abort_( \ + Fail, \ + CRITERION_ASSERT_MSG_NO_THROW, \ + (CR_STR(Statement), CR_STR(Exception)), \ + __VA_ARGS__)); \ + } + +# define cr_assert_throw_va_(...) \ + CR_EXPAND(cr_assert_throw_( \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ + dummy, \ + CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ + )) + +# define cr_assert_no_throw_(Fail, Statement, Exception, ...) \ + try { \ + Statement; \ + } catch (Exception const &) { \ + CR_EXPAND(cr_assert_throw_abort_( \ + Fail, \ + CRITERION_ASSERT_MSG_THROW, \ + (CR_STR(Statement), CR_STR(Exception)), \ + __VA_ARGS__)); \ + } + +# define cr_assert_no_throw_va_(...) \ + CR_EXPAND(cr_assert_no_throw_( \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_HEAD(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))), \ + dummy, \ + CR_VA_TAIL(CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))) \ + )) + +# define cr_assert_any_throw_(Fail, Statement, ...) \ + try { \ + Statement; \ + CR_EXPAND(cr_assert_throw_abort_( \ + Fail, \ + CRITERION_ASSERT_MSG_ANY_THROW, \ + (CR_STR(Statement)), \ + __VA_ARGS__)); \ + } catch (...) {} + +# define cr_assert_any_throw_va_(...) \ + CR_EXPAND(cr_assert_any_throw_( \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + dummy, \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +# define cr_assert_none_throw_(Fail, Statement, ...) \ + try { \ + Statement; \ + } catch (...) { \ + CR_EXPAND(cr_assert_throw_abort_( \ + Fail, \ + CRITERION_ASSERT_MSG_NONE_THROW, \ + (CR_STR(Statement)), \ + __VA_ARGS__)); \ + } + +# define cr_assert_none_throw_va_(...) \ + CR_EXPAND(cr_assert_none_throw_( \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + dummy, \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +// Messages + +# define CRITERION_GNUC_WARN__(Msg) \ + _Pragma(#Msg) + +# define CRITERION_GNUC_WARN_(Name) CRITERION_GNUC_WARN__( \ + message \ + "The `" #Name "` macro is only available on GNU C compilers." \ + ) + +#endif /* !CRITERION_INTERNAL_ASSERT_H_ */ diff --git a/include/criterion/common.h b/include/criterion/internal/common.h similarity index 54% rename from include/criterion/common.h rename to include/criterion/internal/common.h index ae962eb7..f651772e 100644 --- a/include/criterion/common.h +++ b/include/criterion/internal/common.h @@ -42,7 +42,11 @@ # endif # ifdef __cplusplus -# define CR_ATTRIBUTE(Arg) [[gnu::Arg]] +# ifdef __GNUC__ +# define CR_ATTRIBUTE(Arg) __attribute__((Arg)) +# else +# define CR_ATTRIBUTE(Arg) [[gnu::Arg]] +# endif # define CR_BEGIN_C_API extern "C" { # define CR_END_C_API } # else @@ -52,74 +56,74 @@ # endif # ifdef __APPLE__ -# define SECTION_START_PREFIX __first -# define SECTION_END_PREFIX __last -# define SECTION_START_SUFFIX(Name) __asm("section$start$__DATA$" Name) -# define SECTION_END_SUFFIX(Name) __asm("section$end$__DATA$" Name) -# define SECTION_(Name) CR_ATTRIBUTE(section("__DATA," Name)) -# define SECTION_SUFFIX_ +# define CR_SECTION_START_PREFIX __first +# define CR_SECTION_END_PREFIX __last +# define CR_SECTION_START_SUFFIX(Name) __asm("section$start$__DATA$" Name) +# define CR_SECTION_END_SUFFIX(Name) __asm("section$end$__DATA$" Name) +# define CR_SECTION_(Name) CR_ATTRIBUTE(section("__DATA," Name)) +# define CR_SECTION_SUFFIX_ # elif CR_IS_MSVC -# define SECTION_START_PREFIX __start -# define SECTION_END_PREFIX __stop -# define SECTION_START_SUFFIX(Name) -# define SECTION_END_SUFFIX(Name) -# define SECTION_(Name) \ +# define CR_SECTION_START_PREFIX __start +# define CR_SECTION_END_PREFIX __stop +# define CR_SECTION_START_SUFFIX(Name) +# define CR_SECTION_END_SUFFIX(Name) +# define CR_SECTION_(Name) \ __pragma(data_seg(push)) \ __pragma(section(Name, read)) \ __declspec(allocate(Name)) -# define SECTION_SUFFIX_ \ +# define CR_SECTION_SUFFIX_ \ __pragma(data_seg(pop)) # else -# define SECTION_START_PREFIX __start -# define SECTION_END_PREFIX __stop -# define SECTION_START_SUFFIX(Name) -# define SECTION_END_SUFFIX(Name) -# define SECTION_(Name) CR_ATTRIBUTE(section(Name)) -# define SECTION_SUFFIX_ +# define CR_SECTION_START_PREFIX __start +# define CR_SECTION_END_PREFIX __stop +# define CR_SECTION_START_SUFFIX(Name) +# define CR_SECTION_END_SUFFIX(Name) +# define CR_SECTION_(Name) CR_ATTRIBUTE(section(Name)) +# define CR_SECTION_SUFFIX_ # endif -# define MAKE_IDENTIFIER_(Prefix, Id) MAKE_IDENTIFIER__(Prefix, Id) -# define MAKE_IDENTIFIER__(Prefix, Id) Prefix ## _ ## Id +# define CR_MAKE_IDENTIFIER_(Prefix, Id) CR_MAKE_IDENTIFIER__(Prefix, Id) +# define CR_MAKE_IDENTIFIER__(Prefix, Id) Prefix ## _ ## Id -# define SECTION_START_(Name) MAKE_IDENTIFIER_(SECTION_START_PREFIX, Name) -# define SECTION_END_(Name) MAKE_IDENTIFIER_(SECTION_END_PREFIX, Name) +# define CR_SECTION_START_(Name) CR_MAKE_IDENTIFIER_(CR_SECTION_START_PREFIX, Name) +# define CR_SECTION_END_(Name) CR_MAKE_IDENTIFIER_(CR_SECTION_END_PREFIX, Name) -# define SECTION_START(Name) g_ ## Name ## _section_start -# define SECTION_END(Name) g_ ## Name ## _section_end +# define CR_SECTION_START(Name) g_ ## Name ## _section_start +# define CR_SECTION_END(Name) g_ ## Name ## _section_end -# define DECL_SECTION_LIMITS(Type, Name) DECL_SECTION_LIMITS_(Type, Name) -# define DECL_SECTION_LIMITS_(Type, Name) \ - extern Type SECTION_START_(Name) SECTION_START_SUFFIX(#Name); \ - extern Type SECTION_END_(Name) SECTION_END_SUFFIX(#Name) +# define CR_DECL_SECTION_LIMITS(Type, Name) CR_DECL_SECTION_LIMITS_(Type, Name) +# define CR_DECL_SECTION_LIMITS_(Type, Name) \ + extern Type CR_SECTION_START_(Name) CR_SECTION_START_SUFFIX(#Name); \ + extern Type CR_SECTION_END_(Name) CR_SECTION_END_SUFFIX(#Name) -# define IMPL_SECTION_LIMITS(Type, Name) \ - Type *const SECTION_START(Name) = &SECTION_START_(Name); \ - Type *const SECTION_END(Name) = &SECTION_END_(Name) +# define CR_IMPL_SECTION_LIMITS(Type, Name) \ + Type *const CR_SECTION_START(Name) = &CR_SECTION_START_(Name); \ + Type *const CR_SECTION_END(Name) = &CR_SECTION_END_(Name) # ifdef __GNUC__ -# define UNUSED CR_ATTRIBUTE(unused) -# define NORETURN CR_ATTRIBUTE(noreturn) +# define CR_UNUSED CR_ATTRIBUTE(unused) +# define CR_NORETURN CR_ATTRIBUTE(noreturn) # define CR_INLINE CR_ATTRIBUTE(always_inline) inline # elif CR_IS_MSVC -# define UNUSED -# define NORETURN __declspec(noreturn) +# define CR_UNUSED __pragma(warning(suppress:4100)) +# define CR_NORETURN __declspec(noreturn) # define CR_INLINE __forceinline # else -# define UNUSED -# define NORETURN +# define CR_UNUSED +# define CR_NORETURN # define CR_INLINE inline # endif # ifdef _WIN32 -# define SIZE_T_FORMAT "%Iu" +# define CR_SIZE_T_FORMAT "%Iu" # else -# define SIZE_T_FORMAT "%zu" +# define CR_SIZE_T_FORMAT "%zu" # endif # ifdef __GNUC__ -# define FORMAT(Archetype, Index, Ftc) CR_ATTRIBUTE(format(Archetype, Index, Ftc)) +# define CR_FORMAT(Archetype, Index, Ftc) CR_ATTRIBUTE(format(Archetype, Index, Ftc)) # else -# define FORMAT(Archetype, Index, Ftc) +# define CR_FORMAT(Archetype, Index, Ftc) # endif # if defined _WIN32 || defined __CYGWIN__ diff --git a/include/criterion/designated-initializer-compat.h b/include/criterion/internal/designated-initializer-compat.h similarity index 97% rename from include/criterion/designated-initializer-compat.h rename to include/criterion/internal/designated-initializer-compat.h index 9b99d050..e3c59780 100644 --- a/include/criterion/designated-initializer-compat.h +++ b/include/criterion/internal/designated-initializer-compat.h @@ -112,11 +112,11 @@ CR_EXPAND(CRITERION_APPLY(CRITERION_ADD_PREFIX_ONCE, __VA_ARGS__)) # ifdef __cplusplus -# define CRITERION_MAKE_STRUCT(Type, ...) []() { \ - Type t; \ - std::memset(&t, 0, sizeof (t)); \ - CR_EXPAND(CRITERION_ADD_PREFIX(t, __VA_ARGS__)) \ - return t; \ +# define CRITERION_MAKE_STRUCT(Type, ...) []() -> Type { \ + Type t; \ + std::memset(&t, 0, sizeof (t)); \ + CR_EXPAND(CRITERION_ADD_PREFIX(t, __VA_ARGS__)) \ + return t; \ }() # else # define CRITERION_MAKE_STRUCT(Type, ...) { __VA_ARGS__ } diff --git a/include/criterion/internal/hooks.h b/include/criterion/internal/hooks.h new file mode 100644 index 00000000..cf33de53 --- /dev/null +++ b/include/criterion/internal/hooks.h @@ -0,0 +1,84 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_HOOKS_H_ +# define CRITERION_INTERNAL_HOOKS_H_ + +# include "common.h" +# include "../types.h" + +# define CR_HOOK_IDENTIFIER_(Suffix) CR_HOOK_IDENTIFIER__(__LINE__, Suffix) +# define CR_HOOK_IDENTIFIER__(Line, Suffix) CR_HOOK_IDENTIFIER___(Line, Suffix) +# define CR_HOOK_IDENTIFIER___(Line, Suffix) hook_l ## Line ## _ ## Suffix + +# ifdef __cplusplus +# define CR_HOOK_PROTOTYPE_ \ + extern "C" void CR_HOOK_IDENTIFIER_(impl) +# else +# define CR_HOOK_PROTOTYPE_ \ + void CR_HOOK_IDENTIFIER_(impl) +# endif + +// Section abbreviations +# define CR_HOOK_SECTION_PRE_ALL cr_pra +# define CR_HOOK_SECTION_PRE_SUITE cr_prs +# define CR_HOOK_SECTION_PRE_INIT cr_pri +# define CR_HOOK_SECTION_PRE_TEST cr_prt +# define CR_HOOK_SECTION_ASSERT cr_ast +# define CR_HOOK_SECTION_THEORY_FAIL cr_thf +# define CR_HOOK_SECTION_TEST_CRASH cr_tsc +# define CR_HOOK_SECTION_POST_TEST cr_pot +# define CR_HOOK_SECTION_POST_FINI cr_pof +# define CR_HOOK_SECTION_POST_SUITE cr_pos +# define CR_HOOK_SECTION_POST_ALL cr_poa + +# define CR_HOOK_SECTION(Kind) CR_HOOK_SECTION_ ## Kind + +# define CR_HOOK_SECTION_STRINGIFY__(Sec) #Sec +# define CR_HOOK_SECTION_STRINGIFY_(Sec) CR_HOOK_SECTION_STRINGIFY__(Sec) +# define CR_HOOK_SECTION_STRINGIFY(Kind) CR_HOOK_SECTION_STRINGIFY_(CR_HOOK_SECTION(Kind)) + +# define CR_HOOK_PARAM_TYPE_PRE_ALL struct criterion_test_set * +# define CR_HOOK_PARAM_TYPE_PRE_SUITE struct criterion_suite_set * +# define CR_HOOK_PARAM_TYPE_PRE_INIT struct criterion_test * +# define CR_HOOK_PARAM_TYPE_PRE_TEST struct criterion_test * +# define CR_HOOK_PARAM_TYPE_ASSERT struct criterion_assert_stats * +# define CR_HOOK_PARAM_TYPE_THEORY_FAIL struct criterion_theory_stats * +# define CR_HOOK_PARAM_TYPE_TEST_CRASH struct criterion_test_stats * +# define CR_HOOK_PARAM_TYPE_POST_TEST struct criterion_test_stats * +# define CR_HOOK_PARAM_TYPE_POST_FINI struct criterion_test_stats * +# define CR_HOOK_PARAM_TYPE_POST_SUITE struct criterion_suite_stats * +# define CR_HOOK_PARAM_TYPE_POST_ALL struct criterion_global_stats * + +# define CR_HOOK_PARAM_TYPE(Kind) CR_HOOK_PARAM_TYPE_ ## Kind + +# define CR_REPORT_HOOK_IMPL(Kind) \ + CR_HOOK_PROTOTYPE_(CR_HOOK_PARAM_TYPE(Kind)); \ + CR_SECTION_(CR_HOOK_SECTION_STRINGIFY(Kind)) \ + f_report_hook CR_HOOK_IDENTIFIER_(func) = \ + (f_report_hook) CR_HOOK_IDENTIFIER_(impl) \ + CR_SECTION_SUFFIX_; \ + CR_HOOK_PROTOTYPE_ + + +#endif /* !CRITERION_INTERNAL_HOOKS_H_ */ diff --git a/include/criterion/ordered-set.h b/include/criterion/internal/ordered-set.h similarity index 95% rename from include/criterion/ordered-set.h rename to include/criterion/internal/ordered-set.h index 24044f81..aa93cc15 100644 --- a/include/criterion/ordered-set.h +++ b/include/criterion/internal/ordered-set.h @@ -24,7 +24,7 @@ #ifndef CRITERION_ORDERED_SET_H_ # define CRITERION_ORDERED_SET_H_ -# include "types.h" +# include "../types.h" typedef int (*f_criterion_cmp)(void *, void *); @@ -37,7 +37,6 @@ struct criterion_ordered_set { struct criterion_ordered_set_node { struct criterion_ordered_set_node *next; - char data[0]; }; CR_BEGIN_C_API @@ -54,6 +53,6 @@ CR_END_C_API # define FOREACH_SET(Elt, Set) \ for (struct criterion_ordered_set_node *n = Set->first; n; n = n->next) \ for (int cond = 1; cond;) \ - for (Elt = (void*) n->data; cond && (cond = 0, 1);) + for (Elt = (void*) (n + 1); cond && (cond = 0, 1);) #endif /* !CRITERION_ORDERED_SET_H_ */ diff --git a/include/criterion/internal/parameterized.h b/include/criterion/internal/parameterized.h new file mode 100644 index 00000000..a487779d --- /dev/null +++ b/include/criterion/internal/parameterized.h @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_PARAMETERIZED_H_ +# define CRITERION_INTERNAL_PARAMETERIZED_H_ + +# include "test.h" +# include "../types.h" + +struct criterion_test_params { + size_t size; + void *params; + size_t length; + void (*cleanup)(struct criterion_test_params *); + +# ifdef __cplusplus + constexpr criterion_test_params(size_t size, void *params, size_t length) + : size(size) + , params(params) + , length(length) + , cleanup(nullptr) + {} + + constexpr criterion_test_params(size_t size, void *params, size_t length, + void (*cleanup)(struct criterion_test_params *)) + : size(size) + , params(params) + , length(length) + , cleanup(cleanup) + {} + + template + constexpr criterion_test_params(std::vector>& vec, + void (*cleanup)(criterion_test_params *) = nullptr) + : size(sizeof (T)) + , params(&vec[0]) + , length(vec.size()) + , cleanup(cleanup) + {} + + template + constexpr criterion_test_params(T (&arr)[N], + void (*cleanup)(criterion_test_params *) = nullptr) + : size(sizeof (arr[0])) + , params(static_cast(&arr)) + , length(N) + , cleanup(cleanup) + {} +# endif +}; + +# ifdef __cplusplus +# define CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) \ + extern "C" void CR_IDENTIFIER_(Category, Name, impl)(Param) +# else +# define CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) \ + void CR_IDENTIFIER_(Category, Name, impl)(Param) +# endif + +# define CR_PARAM_TEST_BASE(Param, Category, Name, ...) \ + CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name); \ + CR_TEST_TRAMPOLINE_(Category, Name) \ + struct criterion_test_extra_data CR_IDENTIFIER_(Category, Name, extra) = \ + CR_EXPAND(CRITERION_MAKE_STRUCT(criterion_test_extra_data, \ + .lang_ = CR_LANG, \ + .kind_ = CR_TEST_PARAMETERIZED, \ + .param_ = CR_IDENTIFIER_(Category, Name, param), \ + .identifier_ = #Category "/" #Name, \ + .file_ = __FILE__, \ + .line_ = __LINE__, \ + __VA_ARGS__ \ + )); \ + struct criterion_test CR_IDENTIFIER_(Category, Name, meta) = { \ + #Name, \ + #Category, \ + CR_IDENTIFIER_(Category, Name, jmp), \ + &CR_IDENTIFIER_(Category, Name, extra) \ + }; \ + CR_SECTION_("cr_tst") \ + struct criterion_test *CR_IDENTIFIER_(Category, Name, ptr) \ + = &CR_IDENTIFIER_(Category, Name, meta) CR_SECTION_SUFFIX_; \ + CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) + +# define CR_PARAM_TEST_PARAMS(Category, Name) \ + static struct criterion_test_params CR_IDENTIFIER_(Category, Name, param)(void) + +# ifdef __cplusplus +# define cr_make_param_array_(Type, Array, ...) \ + criterion_test_params(sizeof (Type), (Array), __VA_ARGS__) +# else +# define cr_make_param_array_(Type, Array, ...) \ + (struct criterion_test_params) { .size = sizeof (Type), (void*)(Array), __VA_ARGS__ } +# endif + +#endif /* !CRITERION_INTERNAL_PARAMETERIZED_H_ */ diff --git a/include/criterion/preprocess.h b/include/criterion/internal/preprocess.h similarity index 100% rename from include/criterion/preprocess.h rename to include/criterion/internal/preprocess.h diff --git a/include/criterion/internal/redirect.h b/include/criterion/internal/redirect.h new file mode 100644 index 00000000..4d4914d2 --- /dev/null +++ b/include/criterion/internal/redirect.h @@ -0,0 +1,71 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_REDIRECT_H_ +# define CRITERION_INTERNAL_REDIRECT_H_ + +# include "common.h" +# include "assert.h" + +# define cr_assert_redir_op_(Fail, Fun, Op, File, Str, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + !(Fun((File), (Str)) Op 0), \ + dummy, \ + CRITERION_ASSERT_MSG_FILE_STR_MATCH, \ + (CR_STR(File), Str), \ + __VA_ARGS__ \ + )) + +# define cr_assert_redir_op_va_(Fail, Fun, Op, ...) \ + CR_EXPAND(cr_assert_redir_op_( \ + Fail, \ + Fun, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + +# define cr_assert_redir_f_op_(Fail, Fun, Op, File, Ref, ...) \ + CR_EXPAND(cr_assert_impl( \ + Fail, \ + !(Fun((File), (Ref)) Op 0), \ + dummy, \ + CRITERION_ASSERT_MSG_FILE_MATCH, \ + (CR_STR(File), CR_STR(Ref)), \ + __VA_ARGS__ \ + )) + +# define cr_assert_redir_f_op_va_(Fail, Fun, Op, ...) \ + CR_EXPAND(cr_assert_redir_op_( \ + Fail, \ + Fun, \ + Op, \ + CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ + CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ + )) + + +#endif /* !CRITERION_INTERNAL_REDIRECT_H_ */ diff --git a/include/criterion/internal/stdio_filebuf.hxx b/include/criterion/internal/stdio_filebuf.hxx new file mode 100644 index 00000000..0cb53d7c --- /dev/null +++ b/include/criterion/internal/stdio_filebuf.hxx @@ -0,0 +1,138 @@ +#ifndef CRITERION_INTERNAL_STDIO_FILEBUF_HXX_ +# define CRITERION_INTERNAL_STDIO_FILEBUF_HXX_ + +# include + +namespace criterion { namespace internal { + + template > + class stdio_sync_filebuf : public std::basic_streambuf { + public: + typedef Traits traits; + typedef std::basic_filebuf super; + typedef typename Traits::int_type int_type; + typedef typename Traits::pos_type pos_type; + typedef typename Traits::off_type off_type; + + stdio_sync_filebuf(std::FILE *file) + : file(file) + , lastchar(Traits::eof()) + {} + + stdio_sync_filebuf(stdio_sync_filebuf&& other) = default; + stdio_sync_filebuf& operator=(stdio_sync_filebuf&& other) = default; + + void swap(stdio_sync_filebuf& other) { + super::swap(other); + std::swap(file, other.file); + std::swap(lastchar, other.lastchar); + } + + protected: + int_type syncgetc(); + int_type syncungetc(int_type); + int_type syncputc(int_type); + + virtual std::streampos seekoff(std::streamoff off, + std::ios_base::seekdir dir, + std::ios_base::openmode = std::ios_base::in | std::ios_base::out) { + + int whence; + if (dir == std::ios_base::beg) + whence = SEEK_SET; + else if (dir == std::ios_base::cur) + whence = SEEK_CUR; + else + whence = SEEK_END; + + if (!fseek(file, off, whence)) + return std::streampos(std::ftell(file)); + return std::streamoff(-1); + } + + virtual std::streampos seekpos(std::streampos pos, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { + return seekoff(std::streamoff(pos), std::ios_base::beg, mode); + } + + virtual std::streamsize xsgetn(CharT* s, std::streamsize n); + virtual std::streamsize xsputn(const CharT* s, std::streamsize n); + + virtual int sync() { + return std::fflush(file); + } + + virtual int_type underflow() { + int_type c = syncgetc(); + return syncungetc(c); + } + + virtual int_type uflow() { + return lastchar = syncgetc(); + } + + static inline bool is_eof(int_type c) { + static const int_type eof = Traits::eof(); + return Traits::eq_int_type(c, eof); + } + + virtual int_type overflow(int_type c = Traits::eof()) { + int_type ret; + if (is_eof(c)) { + if (std::fflush(file)) + ret = Traits::eof(); + else + ret = Traits::not_eof(c); + } else { + ret = syncputc(c); + } + return ret; + } + + virtual int_type pbackfail(int_type c = Traits::eof()) { + int_type ret = syncungetc(is_eof(c) && !is_eof(lastchar) ? lastchar : c); + lastchar = Traits::eof(); + return ret; + } + + private: + std::FILE *file; + bool file_open; + int_type lastchar; + }; + + template <> + inline stdio_sync_filebuf::int_type + stdio_sync_filebuf::syncgetc() { + return std::getc(file); + } + + template <> + inline stdio_sync_filebuf::int_type + stdio_sync_filebuf::syncungetc(stdio_sync_filebuf::int_type c) { + return std::ungetc(c, file); + } + + template <> + inline stdio_sync_filebuf::int_type + stdio_sync_filebuf::syncputc(stdio_sync_filebuf::int_type c) { + return std::putc(c, file); + } + + template <> + inline std::streamsize + stdio_sync_filebuf::xsgetn(char *s, std::streamsize n) { + std::streamsize res = std::fread(s, 1, n, file); + lastchar = res > 0 ? traits::to_int_type(s[res - 1]) : traits::eof(); + return res; + } + + template <> + inline std::streamsize + stdio_sync_filebuf::xsputn(const char *s, std::streamsize n) { + return std::fwrite(s, 1, n, file); + } + +}} + +#endif /* !CRITERION_INTERNAL_STDIO_FILEBUF_HXX_ */ diff --git a/include/criterion/internal/stream.hxx b/include/criterion/internal/stream.hxx new file mode 100644 index 00000000..828dbdc4 --- /dev/null +++ b/include/criterion/internal/stream.hxx @@ -0,0 +1,110 @@ +#ifndef CRITERION_INTERNAL_STREAM_HXX_ +# define CRITERION_INTERNAL_STREAM_HXX_ + +# include +# include +# include + +# include "stdio_filebuf.hxx" + +namespace criterion { namespace internal { + + template + class stream_mixin : public Super { +public: + stream_mixin(FILE* f) + : Super() + , fbuf(new stdio_sync_filebuf(f)) + , file(f) + { + std::ios::rdbuf(&*fbuf); + } + +# if __cplusplus > 199711L + stream_mixin(const stream_mixin& other) = delete; + stream_mixin& operator=(const stream_mixin& other) = delete; +# endif + + stream_mixin(stream_mixin&& other) : + fbuf(std::move(other.fbuf)), + file(std::move(other.file)) + {} + + stream_mixin& operator=(stream_mixin&& other) { + fbuf = std::move(other.fbuf); + file = std::move(other.file); + } + + void close(void) { + Super::flush(); + Super::close(); + std::fclose(file); + } + + private: + std::shared_ptr> fbuf; + std::FILE* file; + }; + + template + class basic_ofstream : public stream_mixin> { + typedef stream_mixin> super; + public: + basic_ofstream(FILE* f) + : super(f) + {} + + basic_ofstream(basic_ofstream&& other) + : super(std::move(other)) + {} + }; + + template + class basic_ifstream : public stream_mixin> { + typedef stream_mixin> super; + public: + basic_ifstream(FILE* f) + : super(f) + {} + + basic_ifstream(basic_ifstream&& other) + : super(std::move(other)) + {} + }; + + template + class basic_fstream : public stream_mixin> { + typedef stream_mixin> super; + public: + basic_fstream(FILE* f) + : super(f) + {} + + basic_fstream(basic_fstream&& other) + : super(std::move(other)) + {} + }; + + struct get_redirected_out_stream_ { + static inline basic_ofstream& call(std::FILE* f) { + static std::unique_ptr> stream; + + if (!stream) + stream.reset(new basic_ofstream(f)); + return *stream; + } + + }; + + struct get_redirected_in_stream_ { + static inline basic_ifstream& call(std::FILE* f) { + static std::unique_ptr> stream; + if (!stream) + stream.reset(new basic_ifstream(f)); + return *stream; + } + }; + +}} + +#endif /* !CRITERION_INTERNAL_STREAM_HXX_ */ diff --git a/include/criterion/internal/test.h b/include/criterion/internal/test.h new file mode 100644 index 00000000..35b49ae1 --- /dev/null +++ b/include/criterion/internal/test.h @@ -0,0 +1,185 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_TEST_H_ +# define CRITERION_INTERNAL_TEST_H_ + +# include "designated-initializer-compat.h" +# include "common.h" + +# ifdef __OBJC__ +#import +# endif + +# ifdef __cplusplus +# include +# endif + +# define CR_IDENTIFIER_(Category, Name, Suffix) \ + Category ## _ ## Name ## _ ## Suffix + +# ifdef __cplusplus +# ifdef __OBJC__ +# define CR_LANG CR_LANG_OBJCXX +# else +# define CR_LANG CR_LANG_CXX +# endif +# else +# ifdef __OBJC__ +# define CR_LANG CR_LANG_OBJC +# else +# define CR_LANG CR_LANG_C +# endif +# endif + +# ifdef __cplusplus +# define CR_TEST_PROTOTYPE_(Category, Name) \ + extern "C" void CR_IDENTIFIER_(Category, Name, impl)(void) +# else +# define CR_TEST_PROTOTYPE_(Category, Name) \ + void CR_IDENTIFIER_(Category, Name, impl)(void) +# endif + +# define CR_SUITE_IDENTIFIER_(Name, Suffix) \ + suite_ ## Name ## _ ## Suffix + +CR_BEGIN_C_API + +CR_API void criterion_internal_test_setup(void); +CR_API void criterion_internal_test_main(void (*fn)(void)); +CR_API void criterion_internal_test_teardown(void); + +CR_END_C_API + +static const char *const cr_msg_test_init_std_exception = "Caught an unexpected exception during the test initialization: %s."; +static const char *const cr_msg_test_init_other_exception = "Caught some unexpected exception during the test initialization."; +static const char *const cr_msg_test_main_std_exception = "Caught an unexpected exception during the test execution: %s."; +static const char *const cr_msg_test_main_other_exception = "Caught some unexpected exception during the test execution."; +static const char *const cr_msg_test_fini_std_exception = "Caught an unexpected exception during the test finalization: %s."; +static const char *const cr_msg_test_fini_other_exception = "Caught some unexpected exception during the test finalization."; + +# ifdef __cplusplus +# define CR_TEST_TRAMPOLINE_(Category, Name) \ + static inline void CR_IDENTIFIER_(Category, Name, jmp)(void) { \ + try { \ + criterion_internal_test_setup(); \ + } catch (const std::exception &e) { \ + criterion_test_die(cr_msg_test_init_std_exception, e.what()); \ + } catch (...) { \ + criterion_test_die(cr_msg_test_init_other_exception); \ + } \ + try { \ + criterion_internal_test_main((void(*)(void)) CR_IDENTIFIER_(Category, Name, impl)); \ + } catch (const std::exception &e) { \ + criterion_test_die(cr_msg_test_main_std_exception, e.what()); \ + } catch (...) { \ + criterion_test_die(cr_msg_test_main_other_exception); \ + } \ + try { \ + criterion_internal_test_teardown(); \ + } catch (const std::exception &e) { \ + criterion_test_die(cr_msg_test_fini_std_exception, e.what()); \ + } catch (...) { \ + criterion_test_die(cr_msg_test_fini_other_exception); \ + } \ + } +# else +# if defined(__OBJC__) && defined(__EXCEPTIONS) +# define CR_TEST_TRAMPOLINE_(Category, Name) \ + static inline void CR_IDENTIFIER_(Category, Name, jmp)(void) { \ + @try { \ + criterion_internal_test_setup(); \ + } @catch (NSException *e) { \ + NSString *reason = [e reason]; \ + criterion_test_die(cr_msg_test_init_std_exception, [reason UTF8String]); \ + } @catch (...) { \ + criterion_test_die(cr_msg_test_init_other_exception); \ + } \ + @try { \ + criterion_internal_test_main((void(*)(void)) CR_IDENTIFIER_(Category, Name, impl)); \ + } @catch (NSException *e) { \ + NSString *reason = [e reason]; \ + criterion_test_die(cr_msg_test_main_std_exception, [reason UTF8String]); \ + } @catch (...) { \ + criterion_test_die(cr_msg_test_main_other_exception); \ + } \ + @try { \ + criterion_internal_test_teardown(); \ + } @catch (NSException *e) { \ + NSString *reason = [e reason]; \ + criterion_test_die(cr_msg_test_fini_std_exception, [reason UTF8String]); \ + } @catch (...) { \ + criterion_test_die(cr_msg_test_fini_other_exception); \ + } \ + } +# else +# define CR_TEST_TRAMPOLINE_(Category, Name) \ + static inline void CR_IDENTIFIER_(Category, Name, jmp)(void) { \ + criterion_internal_test_setup(); \ + criterion_internal_test_main((void(*)(void)) CR_IDENTIFIER_(Category, Name, impl)); \ + criterion_internal_test_teardown(); \ + } +# endif +# endif + +# define CR_TEST_BASE(Category, Name, ...) \ + CR_TEST_PROTOTYPE_(Category, Name); \ + CR_TEST_TRAMPOLINE_(Category, Name) \ + struct criterion_test_extra_data CR_IDENTIFIER_(Category, Name, extra) = \ + CR_EXPAND(CRITERION_MAKE_STRUCT(criterion_test_extra_data, \ + .lang_ = CR_LANG, \ + .kind_ = CR_TEST_NORMAL, \ + .param_ = (struct criterion_test_params(*)(void)) NULL, \ + .identifier_ = #Category "/" #Name, \ + .file_ = __FILE__, \ + .line_ = __LINE__, \ + __VA_ARGS__ \ + )); \ + struct criterion_test CR_IDENTIFIER_(Category, Name, meta) = { \ + #Name, \ + #Category, \ + CR_IDENTIFIER_(Category, Name, jmp), \ + &CR_IDENTIFIER_(Category, Name, extra) \ + }; \ + CR_SECTION_("cr_tst") \ + struct criterion_test *CR_IDENTIFIER_(Category, Name, ptr) \ + = &CR_IDENTIFIER_(Category, Name, meta) CR_SECTION_SUFFIX_; \ + CR_TEST_PROTOTYPE_(Category, Name) + +# define CR_SUITE_BASE(Name, ...) \ + struct criterion_test_extra_data CR_SUITE_IDENTIFIER_(Name, extra) = \ + CR_EXPAND(CRITERION_MAKE_STRUCT(criterion_test_extra_data, \ + .file_ = __FILE__, \ + .line_ = 0, \ + __VA_ARGS__ \ + )); \ + struct criterion_suite CR_SUITE_IDENTIFIER_(Name, meta) = { \ + #Name, \ + &CR_SUITE_IDENTIFIER_(Name, extra), \ + }; \ + CR_SECTION_("cr_sts") \ + struct criterion_suite *CR_SUITE_IDENTIFIER_(Name, ptr) \ + = &CR_SUITE_IDENTIFIER_(Name, meta) CR_SECTION_SUFFIX_ + + +#endif /* !CRITERION_INTERNAL_TEST_H_ */ diff --git a/include/criterion/internal/theories.h b/include/criterion/internal/theories.h new file mode 100644 index 00000000..798034f0 --- /dev/null +++ b/include/criterion/internal/theories.h @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_INTERNAL_THEORIES_H_ +# define CRITERION_INTERNAL_THEORIES_H_ + +# include "test.h" + +# ifdef __cplusplus +# include +using std::size_t; +# else +# include +# endif + +# ifdef __cplusplus +template +constexpr size_t criterion_va_num__(const T &...) { + return sizeof...(T); +} +# endif + +struct criterion_datapoints { + size_t size; + size_t len; + const char *name; + void *arr; +}; + +CR_BEGIN_C_API + +CR_API void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*fnptr)(void)); + +CR_END_C_API + +# ifdef __cplusplus +# define CR_TH_VA_NUM(Type, ...) criterion_va_num__(__VA_ARGS__) +# define CR_TH_TEMP_ARRAY(Type, ...) []() -> Type* { static Type arr[] = { __VA_ARGS__ }; return reinterpret_cast(&arr); }() +# else +# define CR_TH_VA_NUM(Type, ...) sizeof ((Type[]) { __VA_ARGS__ }) / sizeof (Type) +# define CR_TH_TEMP_ARRAY(Type, ...) &(Type[]) { __VA_ARGS__ } +# endif + +# define CR_TH_INTERNAL_TDPS(Category, Name) \ + static struct criterion_datapoints CR_IDENTIFIER_(Category, Name, dps)[] + +# define CR_TH_INTERNAL_TDP(Category, Name) \ + (CR_IDENTIFIER_(Category, Name, dps)) + +# define CR_TH_INTERNAL_DP(Type, ...) { \ + sizeof (Type), \ + CR_EXPAND(CR_TH_VA_NUM(Type, __VA_ARGS__)), \ + #Type, \ + CR_EXPAND(CR_TH_TEMP_ARRAY(Type, __VA_ARGS__)), \ + } + +# define CR_NB_DATAPOINTS(Var) \ + (sizeof (Var) / sizeof (struct criterion_datapoints)) + +# define CR_VAARG_ID(Suffix, Category, Name, ...) \ + CR_IDENTIFIER_(Category, Name, Suffix) + +# define CR_THEORY_BASE(Args, ...) \ + void CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,))Args; \ + CR_EXPAND(CR_TEST_BASE(__VA_ARGS__, .sentinel_ = 0)) { \ + cr_theory_main( \ + CR_EXPAND(CR_VAARG_ID(dps, __VA_ARGS__,)), \ + CR_NB_DATAPOINTS(CR_EXPAND(CR_VAARG_ID(dps, __VA_ARGS__,))), \ + (void(*)(void)) CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,)) \ + ); \ + } \ + void CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,))Args + +#endif /* !CRITERION_INTERNAL_THEORIES_H_ */ diff --git a/include/criterion/logging.h b/include/criterion/logging.h index 17baf4b5..ab78034e 100644 --- a/include/criterion/logging.h +++ b/include/criterion/logging.h @@ -31,13 +31,15 @@ using std::va_list; # include # include # endif -# include "common.h" -# include "ordered-set.h" +# include "internal/common.h" +# include "internal/ordered-set.h" # include "stats.h" enum criterion_logging_level { CRITERION_INFO = 1, CRITERION_IMPORTANT, + + CRITERION_LOG_LEVEL_QUIET = 1 << 30, }; enum criterion_logging_prefix { @@ -47,6 +49,7 @@ enum criterion_logging_prefix { CRITERION_LOGGING_PREFIX_SKIP, CRITERION_LOGGING_PREFIX_PASS, CRITERION_LOGGING_PREFIX_FAIL, + CRITERION_LOGGING_PREFIX_ERR, }; struct criterion_prefix_data { @@ -64,12 +67,12 @@ struct criterion_prefix_data { # define CRIT_FG_BLUE "\33[0;34m" # define CRIT_RESET "\33[0m" -# define FG_BOLD CRIT_COLOR_NORMALIZE(CRIT_FG_BOLD) -# define FG_RED CRIT_COLOR_NORMALIZE(CRIT_FG_RED) -# define FG_GREEN CRIT_COLOR_NORMALIZE(CRIT_FG_GREEN) -# define FG_GOLD CRIT_COLOR_NORMALIZE(CRIT_FG_GOLD) -# define FG_BLUE CRIT_COLOR_NORMALIZE(CRIT_FG_BLUE) -# define RESET CRIT_COLOR_NORMALIZE(CRIT_RESET) +# define CR_FG_BOLD CRIT_COLOR_NORMALIZE(CRIT_FG_BOLD) +# define CR_FG_RED CRIT_COLOR_NORMALIZE(CRIT_FG_RED) +# define CR_FG_GREEN CRIT_COLOR_NORMALIZE(CRIT_FG_GREEN) +# define CR_FG_GOLD CRIT_COLOR_NORMALIZE(CRIT_FG_GOLD) +# define CR_FG_BLUE CRIT_COLOR_NORMALIZE(CRIT_FG_BLUE) +# define CR_RESET CRIT_COLOR_NORMALIZE(CRIT_RESET) # endif CR_BEGIN_C_API @@ -82,13 +85,14 @@ extern const struct criterion_prefix_data g_criterion_logging_prefixes[]; # define CRITERION_PREFIX_SKIP (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_SKIP ]) # define CRITERION_PREFIX_PASS (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_PASS ]) # define CRITERION_PREFIX_FAIL (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_FAIL ]) +# define CRITERION_PREFIX_ERR (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_ERR ]) CR_API void criterion_vlog(enum criterion_logging_level level, const char *msg, va_list args); -FORMAT(printf, 3, 4) +CR_FORMAT(printf, 3, 4) CR_API void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...); -FORMAT(printf, 2, 3) +CR_FORMAT(printf, 2, 3) CR_API void criterion_log(enum criterion_logging_level level, const char *msg, ...); # define criterion_info(...) criterion_log(CRITERION_INFO, __VA_ARGS__) @@ -97,7 +101,9 @@ CR_API void criterion_log(enum criterion_logging_level level, const char *msg, . # define criterion_pinfo(...) criterion_plog(CRITERION_INFO, __VA_ARGS__) # define criterion_pimportant(...) criterion_plog(CRITERION_IMPORTANT, __VA_ARGS__) -struct criterion_output_provider { +# define criterion_perror(...) criterion_plog(CRITERION_IMPORTANT, CRITERION_PREFIX_ERR, __VA_ARGS__) + +struct criterion_logger { void (*log_pre_all )(struct criterion_test_set *set); void (*log_pre_suite )(struct criterion_suite_set *set); void (*log_pre_init )(struct criterion_test *test); @@ -106,6 +112,7 @@ struct criterion_output_provider { void (*log_theory_fail )(struct criterion_theory_stats *stats); void (*log_test_timeout )(struct criterion_test_stats *stats); void (*log_test_crash )(struct criterion_test_stats *stats); + void (*log_test_abort )(struct criterion_test_stats *stats, const char *msg); void (*log_other_crash )(struct criterion_test_stats *stats); void (*log_abnormal_exit)(struct criterion_test_stats *stats); void (*log_post_test )(struct criterion_test_stats *stats); @@ -114,12 +121,10 @@ struct criterion_output_provider { void (*log_post_all )(struct criterion_global_stats *stats); }; -extern struct criterion_output_provider normal_logging; -extern struct criterion_output_provider tap_logging; +extern struct criterion_logger normal_logging; CR_END_C_API -#define NORMAL_LOGGING (&normal_logging) -#define TAP_LOGGING (&tap_logging) +#define CR_NORMAL_LOGGING (&normal_logging) #endif /* !CRITERION_LOGGING_H_ */ diff --git a/include/criterion/options.h b/include/criterion/options.h index 0bdcdeef..fbe90144 100644 --- a/include/criterion/options.h +++ b/include/criterion/options.h @@ -28,18 +28,90 @@ # include "logging.h" struct criterion_options { + + /** + * The current logging threshold. + * + * default: 1 + */ enum criterion_logging_level logging_threshold; - struct criterion_output_provider *output_provider; + + /** + * The logger that will be used during the execution of the runner. + * + * default: normal logger + */ + struct criterion_logger *logger; + + /** + * Don't exit the child immediately after finishing to run the test + * function, and perform a full cleanup. + * + * Useful when tracking memory leaks, and is immediately implied when + * running the process under valgrind. + * + * default: false + */ bool no_early_exit; + + /** + * Always return a success from criterion_run_all_tests. + * + * default: false + */ bool always_succeed; + + /** + * Disable unicode and ansi coloring from the logging system. + * + * default: false + */ bool use_ascii; + + /** + * Exit immediately after the first test failure. + * + * default: false + */ bool fail_fast; + + /** + * Disable all tests not matching this extglob pattern. + * if NULL, don't filter tests. + * + * default: NULL + */ const char *pattern; + + /** + * Only print the base file name compound of the source file containing + * the tests during reporting. + * + * default: false + */ bool short_filename; + + /** + * The maximum number of parallel jobs that the test runner will spawn. + * 0 means that this number shall be the number of cores on your system. + * + * default: 0 + */ + size_t jobs; + + /** + * Measure and report times. + * + * default: true + */ + bool measure_time; }; CR_BEGIN_C_API +/** + * The runtime options for the test runner. + */ extern struct criterion_options criterion_options; CR_END_C_API diff --git a/include/criterion/output.h b/include/criterion/output.h new file mode 100644 index 00000000..e6366d16 --- /dev/null +++ b/include/criterion/output.h @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_OUTPUT_H_ +# define CRITERION_OUTPUT_H_ + +# include "stats.h" + +typedef void criterion_reporter(FILE *stream, struct criterion_global_stats *); + +/** + * Register an output provider. + * + * @param[in] name The name the output provider shall be registered as. + * @param[in] reporter The output reporting function. + * @returns 1 if no output provider is registered at that name, 0 otherwise, + * and -1 on error. + */ +int criterion_register_output_provider(const char *name, criterion_reporter *reporter); + +/** + * Use an output provider to write a report in a specific path. + * + * @param[in] provider The name of a registered output provider. + * @param[in] path The path to the file to write the report to. + * @returns -1 on error. + */ +int criterion_add_output(const char *provider, const char *path); + +#endif /* !CRITERION_OUTPUT_H_ */ diff --git a/include/criterion/parameterized.h b/include/criterion/parameterized.h index 6bfec4ef..2a9fafff 100644 --- a/include/criterion/parameterized.h +++ b/include/criterion/parameterized.h @@ -1,50 +1,83 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #ifndef CRITERION_PARAMETERIZED_H_ # define CRITERION_PARAMETERIZED_H_ -# include "criterion.h" +# include "alloc.h" +# include "assert.h" +# include "internal/parameterized.h" -# ifdef __cplusplus -# define CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) \ - extern "C" void IDENTIFIER_(Category, Name, impl)(Param) -# else -# define CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) \ - void IDENTIFIER_(Category, Name, impl)(Param) -# endif +/** + * ParameterizedTest(Type *param, Suite, Name, [Options...]) { Function Body } + * + * Defines a new parameterized test. + * + * A parameterized test only takes one parameter -- to pass multiple parameters, + * use a structure type. + * + * @param Type The type of the parameter. + * @param Suite The name of the test suite containing this test. + * @param Name The name of the test. + * @param Options An optional sequence of designated initializer key/value + * pairs as described in the `criterion_test_extra_data` structure + * (see criterion/types.h). + * Example: .exit_code = 1 + */ +# define ParameterizedTest(...) CR_EXPAND(CR_PARAM_TEST_BASE(__VA_ARGS__, .sentinel_ = 0)) -# define ParameterizedTest(...) \ - CR_EXPAND(ParameterizedTest_(__VA_ARGS__, .sentinel_ = 0)) +/** + * ParameterizedTestParameters(Suite, Test) { Function Body } + * + * Defines the parameter generator for the associated parameterized test. + * + * @param Suite The name of the test suite containing the test. + * @param Test The name of the test. + * @returns A constructed instance of criterion::parameters, or the result of + * the cr_make_param_array macro. + */ +# define ParameterizedTestParameters(Suite, Name) CR_PARAM_TEST_PARAMS(Suite, Name) -# define ParameterizedTest_(Param, Category, Name, ...) \ - CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name); \ - struct criterion_test_extra_data IDENTIFIER_(Category, Name, extra) = \ - CR_EXPAND(CRITERION_MAKE_STRUCT(struct criterion_test_extra_data, \ - .kind_ = CR_TEST_PARAMETERIZED, \ - .param_ = IDENTIFIER_(Category, Name, param), \ - .identifier_ = #Category "/" #Name, \ - .file_ = __FILE__, \ - .line_ = __LINE__, \ - __VA_ARGS__ \ - )); \ - struct criterion_test IDENTIFIER_(Category, Name, meta) = { \ - #Name, \ - #Category, \ - (void(*)(void)) IDENTIFIER_(Category, Name, impl), \ - &IDENTIFIER_(Category, Name, extra) \ - }; \ - SECTION_("cr_tst") \ - struct criterion_test *IDENTIFIER_(Category, Name, ptr) \ - = &IDENTIFIER_(Category, Name, meta) SECTION_SUFFIX_; \ - CR_PARAM_TEST_PROTOTYPE_(Param, Category, Name) - -# define ParameterizedTestParameters(Category, Name) \ - static struct criterion_test_params IDENTIFIER_(Category, Name, param)(void) +/** + * cr_make_param_array(Type, Array, Len, [Cleanup]); + * + * Constructs a parameter list used as a return value for a parameter generator. + * + * @param Type The type of the array subscript. + * @param Array The array of parameters. + * @param Len The length of the array. + * @param Cleanup The optional cleanup function for the array. + * @returns The parameter list. + */ +# define cr_make_param_array(...) CR_EXPAND(cr_make_param_array_(__VA_ARGS__)) # ifdef __cplusplus -# define cr_make_param_array(Type, Array, ...) \ - criterion_test_params(sizeof (Type), (Array), __VA_ARGS__) -# else -# define cr_make_param_array(Type, Array, ...) \ - (struct criterion_test_params) { .size = sizeof (Type), (void*)(Array), __VA_ARGS__ } +# include + +namespace criterion { + template + using parameters = std::vector>; +} # endif #endif /* !CRITERION_PARAMETERIZED_H_ */ diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h index 4851b3dc..3d26d97d 100644 --- a/include/criterion/redirect.h +++ b/include/criterion/redirect.h @@ -24,86 +24,82 @@ #ifndef CRITERION_REDIRECT_H_ # define CRITERION_REDIRECT_H_ -# include "common.h" -# include "assert.h" +# include "internal/common.h" +# include "internal/redirect.h" # ifdef __cplusplus # include -# include -# include - -# ifdef __GNUC__ -# if defined(__MINGW32__) || defined(__MINGW64__) -# define off_t _off_t -# define off64_t _off64_t -# endif -# include -# if defined(__MINGW32__) || defined(__MINGW64__) -# undef off_t -# undef off64_t -# endif -# endif # else # include # endif CR_BEGIN_C_API +/** + * Redirect stdout for testing. + */ CR_API void cr_redirect_stdout(void); + +/** + * Redirect stderr for testing. + */ CR_API void cr_redirect_stderr(void); + +/** + * Redirect stdin for testing. + * This is implicitely called before each test. + */ CR_API void cr_redirect_stdin(void); +/** + * Get a file handle representing the read-end of the redirected stdout. + * + * @returns the file handle. + */ CR_API CR_STDN FILE* cr_get_redirected_stdout(void); + +/** + * Get a file handle representing the read-end of the redirected stderr. + * + * @returns the file handle. + */ CR_API CR_STDN FILE* cr_get_redirected_stderr(void); + +/** + * Get a file handle representing the write-end of the redirected stdin. + * + * @returns the file handle. + */ CR_API CR_STDN FILE* cr_get_redirected_stdin(void); +/** + * Compare the contents of a file with a string. + * + * @param[in] f The file to compare the contents to. + * @param[in] str The string to compare the contents to. + * @returns 1 if the contents of the file is equal to the string, 0 otherwise. + */ CR_API int cr_file_match_str(CR_STDN FILE* f, const char *str); + +/** + * Compare the contents of a file with the contents of another file. + * + * @param[in] f The first file to compare the contents to. + * @param[in] ref The second file to compare the contents to. + * @returns 1 if the contents of the files are equal, 0 otherwise. + */ CR_API int cr_file_match_file(CR_STDN FILE* f, CR_STDN FILE* ref); +/** + * Create a file mock. + * + * @param[in] max_size The maximum size in bytes of the file mock. + * @returns the file handle representing the mock. + */ CR_API CR_STDN FILE *cr_mock_file_size(size_t max_size); CR_END_C_API -# define cr_assert_redir_op_(Fail, Fun, Op, File, Str, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - !(Fun((File), (Str)) Op 0), \ - dummy, \ - CRITERION_ASSERT_MSG_FILE_STR_MATCH, \ - (CR_STR(File), Str), \ - __VA_ARGS__ \ - )) - -# define cr_assert_redir_op_va_(Fail, Fun, Op, ...) \ - CR_EXPAND(cr_assert_redir_op_( \ - Fail, \ - Fun, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) - -# define cr_assert_redir_f_op_(Fail, Fun, Op, File, Ref, ...) \ - CR_EXPAND(cr_assert_impl( \ - Fail, \ - !(Fun((File), (Ref)) Op 0), \ - dummy, \ - CRITERION_ASSERT_MSG_FILE_MATCH, \ - (CR_STR(File), CR_STR(Ref)), \ - __VA_ARGS__ \ - )) - -# define cr_assert_redir_f_op_va_(Fail, Fun, Op, ...) \ - CR_EXPAND(cr_assert_redir_op_( \ - Fail, \ - Fun, \ - Op, \ - CR_VA_HEAD(__VA_ARGS__), \ - CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__)), \ - CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__)) \ - )) - # define cr_assert_file_contents_eq_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_ABORT_, cr_file_match_str, ==, __VA_ARGS__)) # define cr_expect_file_contents_eq_str(...) CR_EXPAND(cr_assert_redir_op_va_(CR_FAIL_CONTINUES_, cr_file_match_str, ==, __VA_ARGS__)) @@ -141,134 +137,24 @@ CR_END_C_API # define cr_expect_stderr_neq(...) CR_EXPAND(cr_assert_redir_f_op_va_(CR_FAIL_CONTINUES_, cr_file_match_file, !=, cr_get_redirected_stderr(), __VA_ARGS__)) # ifdef __cplusplus -namespace criterion { +# include "internal/stream.hxx" - template - class stream_mixin : public Super { -public: - stream_mixin(FILE* f) -# ifdef __GNUC__ - : Super() - , fbuf(new ::__gnu_cxx::stdio_sync_filebuf(f)) -# else - : Super(f) -# endif - , file(f) - { -# ifdef __GNUC__ - std::ios::rdbuf(&*fbuf); -# endif - } - - stream_mixin(const stream_mixin& other) = delete; - stream_mixin& operator=(const stream_mixin& other) = delete; - - stream_mixin(stream_mixin&& other) : -# ifdef __GNUC__ - fbuf(std::move(other.fbuf)), -# endif - file(std::move(other.file)) - {} +namespace criterion { - stream_mixin& operator=(stream_mixin&& other) { -# ifdef __GNUC__ - fbuf = std::move(other.fbuf); -# endif - file = std::move(other.file); - } - - void close(void) { - Super::flush(); - Super::close(); - std::fclose(file); - } - - private: -# ifdef __GNUC__ - std::shared_ptr<::__gnu_cxx::stdio_sync_filebuf> fbuf; -# endif - std::FILE* file; - }; - - template - using ofstream_mixin = stream_mixin>; - - template - using ifstream_mixin = stream_mixin>; - - template - using fstream_mixin = stream_mixin>; - - template - class basic_ofstream : public ofstream_mixin { - public: - basic_ofstream(FILE* f) - : ofstream_mixin(f) - {} - - basic_ofstream(basic_ofstream&& other) - : ofstream_mixin(std::move(other)) - {} - }; - - template - class basic_ifstream : public ifstream_mixin { - public: - basic_ifstream(FILE* f) - : ifstream_mixin(f) - {} - - basic_ifstream(basic_ifstream&& other) - : ifstream_mixin(std::move(other)) - {} - }; - - template - class basic_fstream : public fstream_mixin { - public: - basic_fstream(FILE* f) - : fstream_mixin(f) - {} - - basic_fstream(basic_fstream&& other) - : fstream_mixin(std::move(other)) - {} - }; - - using ofstream = basic_ofstream; - using ifstream = basic_ifstream; - using fstream = basic_fstream; - - struct get_redirected_out_stream_ { - static inline ofstream& call(std::FILE* f) { - static std::unique_ptr stream; - - if (!stream) - stream.reset(new ofstream(f)); - return *stream; - } - - }; - - struct get_redirected_in_stream_ { - static inline ifstream& call(std::FILE* f) { - static std::unique_ptr stream; - if (!stream) - stream.reset(new ifstream(f)); - return *stream; - } - }; + typedef internal::basic_ofstream ofstream; + typedef internal::basic_ifstream ifstream; + typedef internal::basic_fstream fstream; static inline ofstream& get_redirected_cin(void) { - return get_redirected_out_stream_::call(cr_get_redirected_stdin()); + return internal::get_redirected_out_stream_::call(cr_get_redirected_stdin()); } static inline ifstream& get_redirected_cout(void) { - return get_redirected_in_stream_::call(cr_get_redirected_stdout()); + return internal::get_redirected_in_stream_::call(cr_get_redirected_stdout()); } static inline ifstream& get_redirected_cerr(void) { - return get_redirected_in_stream_::call(cr_get_redirected_stderr()); + return internal::get_redirected_in_stream_::call(cr_get_redirected_stderr()); } # if __GNUC__ >= 5 diff --git a/include/criterion/stats.h b/include/criterion/stats.h index ec80c2ff..e21a50fc 100644 --- a/include/criterion/stats.h +++ b/include/criterion/stats.h @@ -45,6 +45,7 @@ struct criterion_test_stats { int exit_code; float elapsed_time; bool timed_out; + bool crashed; unsigned progress; const char *file; diff --git a/include/criterion/theories.h b/include/criterion/theories.h index 29055b53..52b61832 100644 --- a/include/criterion/theories.h +++ b/include/criterion/theories.h @@ -24,71 +24,71 @@ #ifndef CRITERION_THEORIES_H_ # define CRITERION_THEORIES_H_ -# ifdef __cplusplus -# include -using std::size_t; -# else -# include -# endif - # include "criterion.h" - -# ifdef __cplusplus -template -constexpr size_t criterion_va_num__(const T &...) { - return sizeof...(T); -} -# endif +# include "internal/theories.h" CR_BEGIN_C_API -struct criterion_theory_context; - -CR_API struct criterion_theory_context* cr_theory_init(void); -CR_API void cr_theory_push_arg(struct criterion_theory_context *ctx, bool is_float, size_t size, void *ptr); -CR_API void cr_theory_free(struct criterion_theory_context *ctx); +/** + * Aborts the current theory iteration. + * This function does not return. + */ CR_API void cr_theory_abort(void); -CR_API int cr_theory_mark(void); -CR_API void cr_theory_reset(struct criterion_theory_context *ctx); -CR_API void cr_theory_call(struct criterion_theory_context *ctx, void (*fnptr)(void)); - -# define TheoryDataPoints(Category, Name) \ - static struct criterion_datapoints IDENTIFIER_(Category, Name, dps)[] +CR_END_C_API -# define TheoryDataPoint(Category, Name) \ - (IDENTIFIER_(Category, Name, dps)) +// Theory and datapoint macros -# ifdef __cplusplus -# define CR_TH_VA_NUM(Type, ...) criterion_va_num__(__VA_ARGS__) -# define CR_TH_TEMP_ARRAY(Type, ...) []() { static Type arr[] = { __VA_ARGS__ }; return &arr; }() -# else -# define CR_TH_VA_NUM(Type, ...) sizeof ((Type[]) { __VA_ARGS__ }) / sizeof (Type) -# define CR_TH_TEMP_ARRAY(Type, ...) &(Type[]) { __VA_ARGS__ } -# endif +/** + * Theory((Params...), Suite, Name, [Options...]) { Function Body } + * + * Defines a new theory test. + * + * The parameters are selected from a cartesian product defined by a + * TheoryDataPoints macro. + * + * @param Params A list of function parameters. + * @param Suite The name of the test suite containing this test. + * @param Name The name of the test. + * @param Options An optional sequence of designated initializer key/value + * pairs as described in the `criterion_test_extra_data` structure + * (see criterion/types.h). + * Example: .exit_code = 1 + */ +# define Theory(Args, ...) CR_EXPAND(CR_THEORY_BASE(Args, __VA_ARGS__)) -# define DataPoints(Type, ...) { \ - sizeof (Type), \ - CR_EXPAND(CR_TH_VA_NUM(Type, __VA_ARGS__)), \ - #Type, \ - CR_EXPAND(CR_TH_TEMP_ARRAY(Type, __VA_ARGS__)), \ - } +/** + * TheoryDataPoints(Suite, Name) = { Datapoints... }; + * + * Defines an array of data points. + * + * The types of the specified data points *must* match the types of the + * associated theory. + * + * Each entry in the array must be the result of the DataPoints macro. + * + * @param Suite The name of the test suite containing this test. + * @param Name The name of the test. + */ +# define TheoryDataPoints(Category, Name) CR_TH_INTERNAL_TDPS(Category, Name) -struct criterion_datapoints { - size_t size; - size_t len; - const char *name; - void *arr; -}; +/** + * DataPoints(Type, Values...) + * + * Defines a new set of data points. + * + * @param Type The type of each data point in the set. + * @param Values The data points in the set. + */ +# define DataPoints(Type, ...) CR_EXPAND(CR_TH_INTERNAL_DP(Type, __VA_ARGS__)) -# define CR_NB_DATAPOINTS(Var) \ - (sizeof (Var) / sizeof (struct criterion_datapoints)) +// Theory invariants # define cr_assume(Condition) \ do { \ if (!(Condition)) \ cr_theory_abort(); \ - } while (0); + } while (0) # define cr_assume_not(Condition) cr_assume(!(Condition)) @@ -111,35 +111,31 @@ struct criterion_datapoints { cr_assume((Expected) - (Actual) > (Epsilon) \ || (Actual) - (Expected) > (Epsilon)) -# define cr_assume_strings_op_(Op, Actual, Expected) \ +# define cr_assume_str_op_(Op, Actual, Expected) \ cr_assume(strcmp((Actual), (Expected)) Op 0) -# define cr_assume_strings_eq(Actual, Expected) cr_assume_strings_op_(==, Actual, Expected) -# define cr_assume_strings_neq(Actual, Expected) cr_assume_strings_op_(!=, Actual, Expected) -# define cr_assume_strings_lt(Actual, Expected) cr_assume_strings_op_(<, Actual, Expected) -# define cr_assume_strings_leq(Actual, Expected) cr_assume_strings_op_(<=, Actual, Expected) -# define cr_assume_strings_gt(Actual, Expected) cr_assume_strings_op_(>, Actual, Expected) -# define cr_assume_strings_geq(Actual, Expected) cr_assume_strings_op_(>=, Actual, Expected) +# define cr_assume_str_eq(Actual, Expected) cr_assume_str_op_(==, Actual, Expected) +# define cr_assume_str_neq(Actual, Expected) cr_assume_str_op_(!=, Actual, Expected) +# define cr_assume_str_lt(Actual, Expected) cr_assume_str_op_(<, Actual, Expected) +# define cr_assume_str_leq(Actual, Expected) cr_assume_str_op_(<=, Actual, Expected) +# define cr_assume_str_gt(Actual, Expected) cr_assume_str_op_(>, Actual, Expected) +# define cr_assume_str_geq(Actual, Expected) cr_assume_str_op_(>=, Actual, Expected) -# define cr_assume_arrays_eq(Actual, Expected, Size) cr_assume(!memcmp((A), (B), (Size))) -# define cr_assume_arrays_neq(Actual, Expected, Size) cr_assume(memcmp((A), (B), (Size))) +# define cr_assume_arr_eq(Actual, Expected, Size) cr_assume(!memcmp((A), (B), (Size))) +# define cr_assume_arr_neq(Actual, Expected, Size) cr_assume(memcmp((A), (B), (Size))) -CR_API void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*fnptr)(void)); +// Deprecated -# define CR_VAARG_ID(Suffix, Category, Name, ...) \ - IDENTIFIER_(Category, Name, Suffix) +# ifndef CRITERION_NO_COMPAT +# define cr_assume_strings_eq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_eq, cr_assume_str_eq) cr_assume_str_eq(__VA_ARGS__) +# define cr_assume_strings_neq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_neq, cr_assume_str_neq) cr_assume_str_neq(__VA_ARGS__) +# define cr_assume_strings_lt(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_lt, cr_assume_str_lt) cr_assume_str_lt(__VA_ARGS__) +# define cr_assume_strings_leq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_leq, cr_assume_str_leq) cr_assume_str_leq(__VA_ARGS__) +# define cr_assume_strings_gt(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_gt, cr_assume_str_gt) cr_assume_str_gt(__VA_ARGS__) +# define cr_assume_strings_geq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_strings_geq, cr_assume_str_geq) cr_assume_str_geq(__VA_ARGS__) -# define Theory(Args, ...) \ - void CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,))Args; \ - CR_EXPAND(Test_(__VA_ARGS__, .sentinel_ = 0)) { \ - cr_theory_main( \ - CR_EXPAND(CR_VAARG_ID(dps, __VA_ARGS__,)), \ - CR_NB_DATAPOINTS(CR_EXPAND(CR_VAARG_ID(dps, __VA_ARGS__,))), \ - (void(*)(void)) CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,)) \ - ); \ - } \ - void CR_EXPAND(CR_VAARG_ID(theory, __VA_ARGS__,))Args - -CR_END_C_API +# define cr_assume_arrays_eq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_arrays_eq, cr_assume_arr_eq) cr_assume_arr_eq(__VA_ARGS__) +# define cr_assume_arrays_neq(...) CRITERION_ASSERT_DEPRECATED_B(cr_assume_arrays_neq, cr_assume_arr_neq) cr_assume_arr_neq(__VA_ARGS__) +# endif #endif /* !CRITERION_THEORIES_H_ */ diff --git a/include/criterion/types.h b/include/criterion/types.h index 7e98bc81..4734656c 100644 --- a/include/criterion/types.h +++ b/include/criterion/types.h @@ -24,61 +24,78 @@ #ifndef CRITERION_TYPES_H_ # define CRITERION_TYPES_H_ +# include "alloc.h" # ifdef __cplusplus # include +# include using std::size_t; # else # include # include # endif -# include "common.h" +# include "internal/common.h" +/** + * Enumerates the supported languages for tests + */ +enum criterion_language { + CR_LANG_C, /// C + CR_LANG_CXX, /// C++ + CR_LANG_OBJC, /// Objective-C + CR_LANG_OBJCXX, /// Objective-C++ + + CR_LANG_SIZE_ // leave this at the end +}; + +extern const char *const cr_language_names[CR_LANG_SIZE_]; + +/** + * Enumerates the supported kinds of tests + */ enum criterion_test_kind { CR_TEST_NORMAL, CR_TEST_PARAMETERIZED, }; -struct criterion_test_params { - size_t size; - void *params; - size_t length; - void (*cleanup)(struct criterion_test_params *); - -# ifdef __cplusplus - constexpr criterion_test_params(size_t size, void *params, size_t length) - : size(size) - , params(params) - , length(length) - , cleanup(nullptr) - {} - - constexpr criterion_test_params(size_t size, void *params, size_t length, - void (*cleanup)(struct criterion_test_params *)) - : size(size) - , params(params) - , length(length) - , cleanup(cleanup) - {} -# endif -}; +/** + * Represents a set of parameters for a parameterized test. + */ +struct criterion_test_params; +/** + * Contains all the options that can be set for a test, through + * the Test and TestSuite macros, or other means. + */ struct criterion_test_extra_data { + // Start of private API + /* + * Warning: the fields below are not meant to be set manually. + * Setting them improperly *will* wreck havock in your tests. + * + * You've been warned. + */ int sentinel_; + enum criterion_language lang_; enum criterion_test_kind kind_; struct criterion_test_params (*param_)(void); const char *identifier_; const char *file_; unsigned line_; - void (*init)(void); - void (*fini)(void); - int signal; - int exit_code; - bool disabled; - const char *description; - double timeout; - void *data; + // Enf of private API + + void (*init)(void); /// The setup test fixture + void (*fini)(void); /// The setup test fixture + int signal; /// The expected signal raised by the test (or 0 if none) + int exit_code; /// The expected exit code returned by the test + bool disabled; /// Whether the test is disabled or not + const char *description; /// The description of a test + double timeout; /// A timeout for the test in seconds + void *data; /// Extra user data }; +/** + * Represents a test + */ struct criterion_test { const char *name; const char *category; @@ -86,6 +103,9 @@ struct criterion_test { struct criterion_test_extra_data *data; }; +/** + * Represents a test suite + */ struct criterion_suite { const char *name; struct criterion_test_extra_data *data; @@ -103,6 +123,4 @@ struct criterion_test_set { size_t tests; }; -typedef void (*f_worker_func)(struct criterion_test *, struct criterion_suite *); - #endif /* !CRITERION_TYPES_H_ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index bec15638..d37bcf23 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,5 @@ # List of source files which contain translatable strings. src/log/normal.c src/string/i18n.c +src/core/runner.c +src/io/output.c diff --git a/po/fr.po b/po/fr.po index d9b7f118..6056b865 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: criterion 2.0.0\n" "Report-Msgid-Bugs-To: franklinmathieu+criterion@gmail.com\n" -"POT-Creation-Date: 2015-09-16 21:18+0200\n" +"POT-Creation-Date: 2015-11-27 12:24+0100\n" "PO-Revision-Date: 2015-04-03 17:58+0200\n" "Last-Translator: \n" "Language-Team: French\n" @@ -18,65 +18,70 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: src/log/normal.c:50 +#: src/log/normal.c:42 #, c-format msgid "Criterion v%s\n" msgstr "Criterion v%s\n" -#: src/log/normal.c:51 +#: src/log/normal.c:43 #, c-format msgid " %s\n" msgstr " %s\n" -#: src/log/normal.c:54 src/log/normal.c:56 +#: src/log/normal.c:46 src/log/normal.c:48 #, c-format msgid "%1$s::%2$s\n" msgstr "%1$s::%2$s\n" -#: src/log/normal.c:55 +#: src/log/normal.c:47 #, fuzzy, c-format msgid "%1$s::%2$s: (%3$3.2fs)\n" msgstr "%1$s::%2$s: (%3$3.2fs)\n" -#: src/log/normal.c:57 +#: src/log/normal.c:49 #, c-format msgid "%1$s::%2$s: Test is disabled\n" msgstr "%1$s::%2$s: Le test est désactivé\n" -#: src/log/normal.c:58 +#: src/log/normal.c:50 #, c-format msgid "%1$s::%2$s: Suite is disabled\n" msgstr "%1$s::%2$s: La suite est désactivée\n" -#: src/log/normal.c:59 +#: src/log/normal.c:51 #, c-format msgid "%1$s%2$s%3$s:%4$s%5$d%6$s: Assertion failed: %7$s\n" msgstr "%1$s%2$s%3$s:%4$s%5$d%6$s: Échec d'assertion: %7$s\n" -#: src/log/normal.c:60 +#: src/log/normal.c:52 #, fuzzy, c-format msgid " Theory %1$s::%2$s failed with the following parameters: (%3$s)\n" msgstr "" " La théorie %1$s::%2$s a échoué avec les paramètres suivants: (%3$s)\n" -#: src/log/normal.c:61 +#: src/log/normal.c:53 #, fuzzy, c-format msgid "%1$s::%2$s: Timed out. (%3$3.2fs)\n" msgstr "%1$s::%2$s: Délai expiré. (%3$3.2fs)\n" -#: src/log/normal.c:62 +#: src/log/normal.c:54 #, c-format msgid "%1$s%2$s%3$s:%4$s%5$u%6$s: Unexpected signal caught below this line!\n" msgstr "" "%1$s%2$s%3$s:%4$s%5$u%6$s: Un signal inattendu a été reçu après cette " "ligne!\n" -#: src/log/normal.c:63 +#: src/log/normal.c:55 #, c-format msgid "%1$s::%2$s: CRASH!\n" msgstr "%1$s::%2$s: PLANTAGE!\n" -#: src/log/normal.c:64 +#: src/log/normal.c:56 +#, fuzzy, c-format +msgid "%1$s::%2$s: %3$s\n" +msgstr "%1$s::%2$s: (%3$3.2fs)\n" + +#: src/log/normal.c:57 #, fuzzy, c-format msgid "" "%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown." @@ -85,7 +90,7 @@ msgstr "" "%1$sAttention! Le test `%2$s::%3$s` a planté pendant son initialisation ou " "sa finalisation.%4$s\n" -#: src/log/normal.c:65 +#: src/log/normal.c:58 #, fuzzy, c-format msgid "" "%1$sWarning! The test `%2$s::%3$s` exited during its setup or teardown.%4$s\n" @@ -93,14 +98,14 @@ msgstr "" "%1$sAttention! Le test `%2$s::%3$s` a quitté pendant son initialisation ou " "sa finalisation.%4$s\n" -#: src/log/normal.c:66 +#: src/log/normal.c:59 #, c-format msgid "Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n" msgid_plural "Running %1$s%2$lu%3$s tests from %4$s%5$s%6$s:\n" msgstr[0] "Lancement de %1$s%2$lu%3$s test dans %4$s%5$s%6$s:\n" msgstr[1] "Lancement de %1$s%2$lu%3$s tests dans %4$s%5$s%6$s:\n" -#: src/log/normal.c:68 +#: src/log/normal.c:61 #, c-format msgid "" "%1$sSynthesis: Tested: %2$s%3$lu%4$s | Passing: %5$s%6$lu%7$s | Failing: %8$s" @@ -109,6 +114,11 @@ msgstr "" "%1$sSynthèse: Testés: %2$s%3$lu%4$s | Validés: %5$s%6$lu%7$s | Échoués: %8$s" "%9$lu%10$s | Plantages: %11$s%12$lu%13$s %14$s\n" +#: src/log/normal.c:77 +#, fuzzy, c-format +msgid "%s::%s: %s\n" +msgstr "%1$s::%2$s: (%3$3.2fs)\n" + #: src/string/i18n.c:13 msgid "The conditions for this assertion were not met." msgstr "Les conditions de cette assertion n'ont pas été remplies." @@ -174,3 +184,36 @@ msgstr "L'instruction `%1$s` a levé une instance de l'exception `%2$s`." #, c-format msgid "The statement `%1$s` did not throw an instance of the `%2$s` exception." msgstr "L'instruction `%1$s` n'a pas levé d'instance de l'exception `%2$s`." + +#: src/core/runner.c:58 +#, c-format +msgid "" +"%1$sWarning! Criterion has detected that it is running under valgrind, but " +"the no_early_exit option is explicitely disabled. Reports will not be " +"accurate!%2$s\n" +msgstr "" +"%1$sAttention! Criterion a détecté qu'il a été lancé avec valgrind, mais " +"l'option no_early_exit est explicitement désactivée. Les rapports d'erreur " +"ne seront pas précis!%2$s\n" + +#: src/core/runner.c:62 +#, c-format +msgid "" +"%1$sWarning! Criterion has detected that it is running under valgrind, but " +"the number of jobs have been explicitely set. Reports might appear confusing!" +"%2$s\n" +msgstr "" +"%1$sAttention! Criterion a détecté qu'il a été lancé avec valgrind, mais le " +"nombre de tâches est explicitement défini. Les rapports d'erreur risquent " +"d'être déroutants!%2$s\n" + +#: src/io/output.c:13 +#, fuzzy, c-format +msgid "Could not open the file @ `%1$s` for %2$s reporting: %3$s.\n" +msgstr "" +"Impossible d'ouvrir le fichier `%1$s` pour faire le rapport %2$s: %3$s.\n" + +#: src/io/output.c:14 +#, c-format +msgid "Writing %1$s report in `%2$s`.\n" +msgstr "Écriture du rapport %1$s dans `%2$s`.\n" diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index a80f1afb..799a7589 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,6 +1,6 @@ if (NOT MSVC) - set(CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -pedantic") - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -pedantic") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEFAULT} -std=c99 -Wall -Wextra -pedantic") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEFAULT} ${CXX11_FLAG} -Wall -Wextra -pedantic") endif () include_directories(../include) @@ -14,25 +14,38 @@ set(SAMPLES more-suites.c description.c simple.c - theories.c timeout.c redirect.c parameterized.c - signal.cc - report.cc - suites.cc - fixtures.cc - asserts.cc - more-suites.cc - description.cc - simple.cc - theories.cc - redirect.cc ) +if (CMAKE_CXX_COMPILER_WORKS) + set(SAMPLES ${SAMPLES} + signal.cc + report.cc + suites.cc + fixtures.cc + asserts.cc + more-suites.cc + description.cc + simple.cc + redirect.cc + parameterized.cc + ) +endif () + +if (THEORIES) + set(SAMPLES ${SAMPLES} theories.c) + if (CMAKE_CXX_COMPILER_WORKS) + set(SAMPLES ${SAMPLES} theories.cc) + endif () +endif () + set(SCRIPTS tap_test + xml_test + json_test early_exit verbose list @@ -65,6 +78,8 @@ macro(add_samples DIR_ SAMPLES_) ENVIRONMENT "CRITERION_ALWAYS_SUCCEED=1" ENVIRONMENT "CRITERION_SHORT_FILENAME=1" ENVIRONMENT "CRITERION_NO_EARLY_EXIT=1" # for coverage + ENVIRONMENT "CRITERION_JOBS=1" # for output ordering + ENVIRONMENT "CRITERION_DISABLE_TIME_MEASUREMENTS=1" # do not compare timings ) endif () endforeach() @@ -75,18 +90,19 @@ add_samples("${CMAKE_CURRENT_LIST_DIR}/tests" "${SAMPLES}") if (NOT MSVC) # we disable the scripted tests when building with MSVC foreach(script ${SCRIPTS}) - add_test(${script} sh ${CMAKE_CURRENT_LIST_DIR}/tests/${script}.sh) + add_test(${script} sh "${CMAKE_CURRENT_LIST_DIR}/tests/${script}.sh") set_property(TEST ${script} PROPERTY ENVIRONMENT "CRITERION_ALWAYS_SUCCEED=1" ENVIRONMENT "CRITERION_NO_EARLY_EXIT=1" # for coverage ) - add_test(${script}_compare sh ${CMAKE_CURRENT_LIST_DIR}/tests/run_test.sh "${CMAKE_CURRENT_LIST_DIR}" . "${CMAKE_CURRENT_LIST_DIR}" tests/${script}) + add_test(${script}_compare sh ${CMAKE_CURRENT_LIST_DIR}/tests/run_test.sh "${CMAKE_CURRENT_LIST_DIR}" . "${CMAKE_CURRENT_LIST_DIR}" "tests/${script}.sh") set_property(TEST ${script}_compare PROPERTY ENVIRONMENT "LC_ALL=en_US.utf8" ENVIRONMENT "CRITERION_ALWAYS_SUCCEED=1" ENVIRONMENT "CRITERION_SHORT_FILENAME=1" ENVIRONMENT "CRITERION_NO_EARLY_EXIT=1" # for coverage + ENVIRONMENT "CRITERION_JOBS=1" # for output ordering ) endforeach() diff --git a/samples/outputs/asserts.c.bin.err.expected b/samples/outputs/asserts.c.bin.err.expected index 95d9a944..10e2e904 100644 --- a/samples/outputs/asserts.c.bin.err.expected +++ b/samples/outputs/asserts.c.bin.err.expected @@ -1,7 +1,7 @@ [----] asserts.c:11: Assertion failed: assert is fatal, expect isn't [----] asserts.c:12: Assertion failed: This assert runs -[FAIL] asserts::base: (0.00s) +[FAIL] asserts::base [----] asserts.c:17: Assertion failed: You can fail an assertion with a message from anywhere [----] asserts.c:18: Assertion failed: The conditions for this assertion were not met. -[FAIL] asserts::old_school: (0.00s) +[FAIL] asserts::old_school [====] Synthesis: Tested: 6 | Passing: 4 | Failing: 2 | Crashing: 0  diff --git a/samples/outputs/asserts.cc.bin.err.expected b/samples/outputs/asserts.cc.bin.err.expected index ebeb3553..ddc7623e 100644 --- a/samples/outputs/asserts.cc.bin.err.expected +++ b/samples/outputs/asserts.cc.bin.err.expected @@ -1,11 +1,11 @@ [----] asserts.cc:83: Assertion failed: The expression (&s1)[0 .. 2] == (&s2)[0 .. 2] is false. -[FAIL] asserts::array: (0.00s) +[FAIL] asserts::array [----] asserts.cc:13: Assertion failed: assert is fatal, expect isn't [----] asserts.cc:14: Assertion failed: This assert runs -[FAIL] asserts::base: (0.00s) +[FAIL] asserts::base [----] asserts.cc:89: Assertion failed: The statement `throw std::exception()` did not throw an instance of the `std::bad_alloc` exception. -[FAIL] asserts::exception: (0.00s) +[FAIL] asserts::exception [----] asserts.cc:19: Assertion failed: You can fail an assertion with a message from anywhere [----] asserts.cc:20: Assertion failed: The conditions for this assertion were not met. -[FAIL] asserts::old_school: (0.00s) +[FAIL] asserts::old_school [====] Synthesis: Tested: 7 | Passing: 3 | Failing: 4 | Crashing: 0  diff --git a/samples/outputs/description.c.bin.err.expected b/samples/outputs/description.c.bin.err.expected index 2cc0d104..ffcd02dd 100644 --- a/samples/outputs/description.c.bin.err.expected +++ b/samples/outputs/description.c.bin.err.expected @@ -1,3 +1,3 @@ [----] description.c:4: Assertion failed: The expression 0 is false. -[FAIL] misc::failing: (0.00s) +[FAIL] misc::failing [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/outputs/description.cc.bin.err.expected b/samples/outputs/description.cc.bin.err.expected index b9cebe4b..ff63cc4f 100644 --- a/samples/outputs/description.cc.bin.err.expected +++ b/samples/outputs/description.cc.bin.err.expected @@ -1,3 +1,3 @@ [----] description.cc:4: Assertion failed: The expression 0 is false. -[FAIL] misc::failing: (0.00s) +[FAIL] misc::failing [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/outputs/parameterized.c.bin.err.expected b/samples/outputs/parameterized.c.bin.err.expected index a080c6d6..62992988 100644 --- a/samples/outputs/parameterized.c.bin.err.expected +++ b/samples/outputs/parameterized.c.bin.err.expected @@ -1,19 +1,19 @@ [----] parameterized.c:76: Assertion failed: Parameters: (1, 2.000000) -[FAIL] params::cleanup: (0.00s) +[FAIL] params::cleanup [----] parameterized.c:76: Assertion failed: Parameters: (3, 4.000000) -[FAIL] params::cleanup: (0.00s) +[FAIL] params::cleanup [----] parameterized.c:76: Assertion failed: Parameters: (5, 6.000000) -[FAIL] params::cleanup: (0.00s) +[FAIL] params::cleanup [----] parameterized.c:36: Assertion failed: Parameters: (1, 2.000000) -[FAIL] params::multiple: (0.00s) +[FAIL] params::multiple [----] parameterized.c:36: Assertion failed: Parameters: (3, 4.000000) -[FAIL] params::multiple: (0.00s) +[FAIL] params::multiple [----] parameterized.c:36: Assertion failed: Parameters: (5, 6.000000) -[FAIL] params::multiple: (0.00s) +[FAIL] params::multiple [----] parameterized.c:15: Assertion failed: Parameter: foo -[FAIL] params::str: (0.00s) +[FAIL] params::str [----] parameterized.c:15: Assertion failed: Parameter: bar -[FAIL] params::str: (0.00s) +[FAIL] params::str [----] parameterized.c:15: Assertion failed: Parameter: baz -[FAIL] params::str: (0.00s) +[FAIL] params::str [====] Synthesis: Tested: 9 | Passing: 0 | Failing: 9 | Crashing: 0  diff --git a/samples/outputs/report.c.bin.err.expected b/samples/outputs/report.c.bin.err.expected index f08bf9aa..5de68252 100644 --- a/samples/outputs/report.c.bin.err.expected +++ b/samples/outputs/report.c.bin.err.expected @@ -1,3 +1,3 @@ [----] report.c:5: Assertion failed: The expression 0 is false. -[FAIL] sample::test: (0.00s) +[FAIL] sample::test [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/outputs/report.cc.bin.err.expected b/samples/outputs/report.cc.bin.err.expected index 72c59aa4..0436a29f 100644 --- a/samples/outputs/report.cc.bin.err.expected +++ b/samples/outputs/report.cc.bin.err.expected @@ -1,3 +1,3 @@ [----] report.cc:5: Assertion failed: The expression 0 is false. -[FAIL] sample::test: (0.00s) +[FAIL] sample::test [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/outputs/signal.c.bin.err.expected b/samples/outputs/signal.c.bin.err.expected index 8bc2b303..02d96bb8 100644 --- a/samples/outputs/signal.c.bin.err.expected +++ b/samples/outputs/signal.c.bin.err.expected @@ -1,4 +1,4 @@ [----] signal.c:16: Unexpected signal caught below this line! [FAIL] simple::uncaught: CRASH! -[FAIL] simple::wrong_signal: (0.00s) +[FAIL] simple::wrong_signal [====] Synthesis: Tested: 3 | Passing: 1 | Failing: 2 | Crashing: 1  diff --git a/samples/outputs/signal.cc.bin.err.expected b/samples/outputs/signal.cc.bin.err.expected index 8ac44c18..8ec51039 100644 --- a/samples/outputs/signal.cc.bin.err.expected +++ b/samples/outputs/signal.cc.bin.err.expected @@ -1,4 +1,4 @@ [----] signal.cc:16: Unexpected signal caught below this line! [FAIL] simple::uncaught: CRASH! -[FAIL] simple::wrong_signal: (0.00s) +[FAIL] simple::wrong_signal [====] Synthesis: Tested: 3 | Passing: 1 | Failing: 2 | Crashing: 1  diff --git a/samples/outputs/simple.c.bin.err.expected b/samples/outputs/simple.c.bin.err.expected index a6878985..4898f354 100644 --- a/samples/outputs/simple.c.bin.err.expected +++ b/samples/outputs/simple.c.bin.err.expected @@ -1,3 +1,3 @@ [----] simple.c:4: Assertion failed: The expression 0 is false. -[FAIL] misc::failing: (0.00s) +[FAIL] misc::failing [====] Synthesis: Tested: 2 | Passing: 1 | Failing: 1 | Crashing: 0  diff --git a/samples/outputs/simple.cc.bin.err.expected b/samples/outputs/simple.cc.bin.err.expected index 816d6cd4..e2e6e05b 100644 --- a/samples/outputs/simple.cc.bin.err.expected +++ b/samples/outputs/simple.cc.bin.err.expected @@ -1,3 +1,3 @@ [----] simple.cc:4: Assertion failed: The expression 0 is false. -[FAIL] misc::failing: (0.00s) +[FAIL] misc::failing [====] Synthesis: Tested: 2 | Passing: 1 | Failing: 1 | Crashing: 0  diff --git a/samples/parameterized.cc b/samples/parameterized.cc new file mode 100644 index 00000000..e6aaff95 --- /dev/null +++ b/samples/parameterized.cc @@ -0,0 +1,75 @@ +#include + +// Basic usage + +ParameterizedTestParameters(params, str) { + static const char *strings[] = { + "foo", "bar", "baz" + }; + + return cr_make_param_array(const char *, strings, sizeof (strings) / sizeof (const char *)); +} + +ParameterizedTest(const char **str, params, str) { + cr_assert_fail("Parameter: %s", *str); +} + +// Multiple parameters must be coalesced in a single parameter + +struct parameter_tuple { + int i; + double d; +}; + +ParameterizedTestParameters(params, multiple) { + static struct parameter_tuple params[] = { + {1, 2}, + {3, 4}, + {5, 6}, + }; + + return criterion_test_params(params); +} + +ParameterizedTest(struct parameter_tuple *tup, params, multiple) { + cr_assert_fail("Parameters: (%d, %f)", tup->i, tup->d); +} + +// Using dynamically generated parameters + +// you **MUST** use new_obj, new_arr, delete_obj, delete_arr instead of +// the new, new[], delete and delete[] operators (respectively) to allocate and +// deallocate dynamic memory in parameters, otherwise this will crash on +// Windows builds of the test. + +// the criterion::allocator allocator may be used with STL containers to +// allocate objects with the functions described above. + +using criterion::new_obj; +using criterion::new_arr; +using criterion::delete_obj; +using criterion::delete_arr; + +struct parameter_tuple_dyn { + int i; + std::unique_ptr d; + + parameter_tuple_dyn() : i(0), d(nullptr, criterion::free) {} + parameter_tuple_dyn(int i, double *d) : i(i), d(d, criterion::free) {} +}; + +ParameterizedTestParameters(params, cleanup) { + static criterion::parameters params; + + params.push_back(parameter_tuple_dyn(1, new_obj(2))); + params.push_back(parameter_tuple_dyn(3, new_obj(4))); + params.push_back(parameter_tuple_dyn(5, new_obj(6))); + + // A criterion::parameters can be returned in place of a + // criterion_test_params. + return params; +} + +ParameterizedTest(parameter_tuple_dyn *tup, params, cleanup) { + cr_assert_fail("Parameters: (%d, %f)", tup->i, *tup->d); +} diff --git a/samples/tests/CMakeLists.txt b/samples/tests/CMakeLists.txt index 463d4da7..9432b91e 100644 --- a/samples/tests/CMakeLists.txt +++ b/samples/tests/CMakeLists.txt @@ -4,10 +4,24 @@ set(SAMPLES long-messages.c other-crashes.c - failmessages.cc - exit.cc - long-messages.cc - other-crashes.cc ) +if (CMAKE_CXX_COMPILER_WORKS) + set(SAMPLES ${SAMPLES} + failmessages.cc + exit.cc + long-messages.cc + other-crashes.cc + + exception.cc + ) +endif () + +if (THEORIES) + set(SAMPLES ${SAMPLES} theories_regression.c) + if (CMAKE_CXX_COMPILER_WORKS) + set(SAMPLES ${SAMPLES} theories_regression.cc) + endif () +endif () + add_samples("${CMAKE_CURRENT_LIST_DIR}" "${SAMPLES}") diff --git a/samples/tests/exception.cc b/samples/tests/exception.cc new file mode 100644 index 00000000..2cbf7f31 --- /dev/null +++ b/samples/tests/exception.cc @@ -0,0 +1,18 @@ +#include +#include + +void raise_std(void) { + throw std::invalid_argument("Some exception message"); +} + +void raise_any(void) { + throw 1; +} + +Test(exception, raise_std) { raise_std(); } +Test(exception, raise_any) { raise_any(); } + +Test(exception, raise_std_init, .init = raise_std) {} +Test(exception, raise_any_init, .init = raise_any) {} +Test(exception, raise_std_fini, .fini = raise_std) {} +Test(exception, raise_any_fini, .fini = raise_any) {} diff --git a/samples/tests/json_test.sh b/samples/tests/json_test.sh new file mode 100755 index 00000000..2546c00e --- /dev/null +++ b/samples/tests/json_test.sh @@ -0,0 +1,7 @@ +#!/bin/sh +./simple.c.bin --json --always-succeed +./signal.c.bin --json --always-succeed +./asserts.c.bin --json --always-succeed +./more-suites.c.bin --json --always-succeed +./tests/long-messages.c.bin --json --always-succeed +./description.c.bin --json --always-succeed diff --git a/samples/tests/outputs/failmessages.c.bin.err.expected b/samples/tests/outputs/failmessages.c.bin.err.expected index 3a6b0e7a..fc2dc0c6 100644 --- a/samples/tests/outputs/failmessages.c.bin.err.expected +++ b/samples/tests/outputs/failmessages.c.bin.err.expected @@ -17,7 +17,7 @@ [----] failmessages.c:22: Assertion failed: The expression (as strings) ("abc") <= ("aba") is false. [----] failmessages.c:23: Assertion failed: The expression (as strings) ("abc") > ("abd") is false. [----] failmessages.c:24: Assertion failed: The expression (as strings) ("abc") >= ("abd") is false. -[FAIL] messages::default: (0.00s) +[FAIL] messages::default [----] failmessages.c:28: Assertion failed: foo bar [----] failmessages.c:29: Assertion failed: foo bar [----] failmessages.c:30: Assertion failed: foo bar @@ -37,5 +37,5 @@ [----] failmessages.c:46: Assertion failed: foo bar [----] failmessages.c:47: Assertion failed: foo bar [----] failmessages.c:48: Assertion failed: foo bar -[FAIL] messages::user: (0.00s) +[FAIL] messages::user [====] Synthesis: Tested: 2 | Passing: 0 | Failing: 2 | Crashing: 0  diff --git a/samples/tests/outputs/failmessages.cc.bin.err.expected b/samples/tests/outputs/failmessages.cc.bin.err.expected index f060bace..5a573553 100644 --- a/samples/tests/outputs/failmessages.cc.bin.err.expected +++ b/samples/tests/outputs/failmessages.cc.bin.err.expected @@ -21,7 +21,7 @@ [----] failmessages.cc:28: Assertion failed: The statement `throw std::exception()` did throw an instance of the `std::exception` exception. [----] failmessages.cc:29: Assertion failed: The statement `{}` did not throw any exception. [----] failmessages.cc:30: Assertion failed: The statement `throw std::exception()` threw some exception. -[FAIL] messages::default: (0.00s) +[FAIL] messages::default [----] failmessages.cc:34: Assertion failed: foo bar [----] failmessages.cc:35: Assertion failed: foo bar [----] failmessages.cc:36: Assertion failed: foo bar @@ -45,5 +45,5 @@ [----] failmessages.cc:57: Assertion failed: foo bar [----] failmessages.cc:58: Assertion failed: foo bar [----] failmessages.cc:59: Assertion failed: foo bar -[FAIL] messages::user: (0.00s) +[FAIL] messages::user [====] Synthesis: Tested: 2 | Passing: 0 | Failing: 2 | Crashing: 0  diff --git a/samples/tests/outputs/json_test.sh.err.expected b/samples/tests/outputs/json_test.sh.err.expected new file mode 100644 index 00000000..001f0dc1 --- /dev/null +++ b/samples/tests/outputs/json_test.sh.err.expected @@ -0,0 +1,237 @@ +{ + "id": "Criterion v2.1.0", + "passed": 1, + "failed": 1, + "errored": 0, + "skipped": 0, + "test_suites": [ + { + "name": "misc", + "passed": 1, + "failed": 1, + "errored": 0, + "skipped": 0, + "tests": [ + { + "name": "passing", + "assertions": 1, + "status": "PASSED" + }, + { + "name": "failing", + "assertions": 1, + "status": "FAILED", + "messages": [ + "simple.c:4: The expression 0 is false." + ] + } + ] + } + ] +} +{ + "id": "Criterion v2.1.0", + "passed": 1, + "failed": 2, + "errored": 1, + "skipped": 0, + "test_suites": [ + { + "name": "simple", + "passed": 1, + "failed": 2, + "errored": 1, + "skipped": 0, + "tests": [ + { + "name": "wrong_signal", + "assertions": 0, + "status": "FAILED", + "messages": [ + + ] + }, + { + "name": "uncaught", + "assertions": 0, + "status": "ERRORED", + "messages": ["The test crashed."] + }, + { + "name": "caught", + "assertions": 0, + "status": "PASSED" + } + ] + } + ] +} +{ + "id": "Criterion v2.1.0", + "passed": 4, + "failed": 2, + "errored": 0, + "skipped": 0, + "test_suites": [ + { + "name": "asserts", + "passed": 4, + "failed": 2, + "errored": 0, + "skipped": 0, + "tests": [ + { + "name": "string", + "assertions": 10, + "status": "PASSED" + }, + { + "name": "old_school", + "assertions": 2, + "status": "FAILED", + "messages": [ + "asserts.c:18: The conditions for this assertion were not met.", + "asserts.c:17: You can fail an assertion with a message from anywhere" + ] + }, + { + "name": "native", + "assertions": 8, + "status": "PASSED" + }, + { + "name": "float", + "assertions": 2, + "status": "PASSED" + }, + { + "name": "base", + "assertions": 6, + "status": "FAILED", + "messages": [ + "asserts.c:12: This assert runs", + "asserts.c:11: assert is fatal, expect isn't" + ] + }, + { + "name": "array", + "assertions": 3, + "status": "PASSED" + } + ] + } + ] +} +{ + "id": "Criterion v2.1.0", + "passed": 2, + "failed": 0, + "errored": 0, + "skipped": 1, + "test_suites": [ + { + "name": "suite2", + "passed": 1, + "failed": 0, + "errored": 0, + "skipped": 0, + "tests": [ + { + "name": "test", + "assertions": 1, + "status": "PASSED" + } + ] + }, + { + "name": "suite1", + "passed": 1, + "failed": 0, + "errored": 0, + "skipped": 0, + "tests": [ + { + "name": "test", + "assertions": 1, + "status": "PASSED" + } + ] + }, + { + "name": "disabled", + "passed": 0, + "failed": 0, + "errored": 0, + "skipped": 1, + "tests": [ + { + "name": "test", + "assertions": 0, + "status": "SKIPPED", + "messages": ["The test was skipped."] + } + ] + } + ] +} +{ + "id": "Criterion v2.1.0", + "passed": 0, + "failed": 1, + "errored": 0, + "skipped": 0, + "test_suites": [ + { + "name": "sample", + "passed": 0, + "failed": 1, + "errored": 0, + "skipped": 0, + "tests": [ + { + "name": "long_msg", + "assertions": 1, + "status": "FAILED", + "messages": [ + "long-messages.c:4: This is", + " A long message", + " Spawning multiple lines.", + " Formatting is respected." + ] + } + ] + } + ] +} +{ + "id": "Criterion v2.1.0", + "passed": 0, + "failed": 1, + "errored": 0, + "skipped": 1, + "test_suites": [ + { + "name": "misc", + "passed": 0, + "failed": 1, + "errored": 0, + "skipped": 1, + "tests": [ + { + "name": "skipped", + "assertions": 0, + "status": "SKIPPED", + "messages": ["The test was skipped."] + }, + { + "name": "failing", + "assertions": 1, + "status": "FAILED", + "messages": [ + "description.c:4: The expression 0 is false." + ] + } + ] + } + ] +} diff --git a/samples/tests/outputs/json_test.sh.out.expected b/samples/tests/outputs/json_test.sh.out.expected new file mode 100644 index 00000000..e69de29b diff --git a/samples/tests/outputs/long-messages.c.bin.err.expected b/samples/tests/outputs/long-messages.c.bin.err.expected index 9658ae52..f97a7f4e 100644 --- a/samples/tests/outputs/long-messages.c.bin.err.expected +++ b/samples/tests/outputs/long-messages.c.bin.err.expected @@ -2,5 +2,5 @@ [----] A long message [----] Spawning multiple lines. [----] Formatting is respected. -[FAIL] sample::long_msg: (0.00s) +[FAIL] sample::long_msg [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/tests/outputs/long-messages.cc.bin.err.expected b/samples/tests/outputs/long-messages.cc.bin.err.expected index fa56cf28..0237997c 100644 --- a/samples/tests/outputs/long-messages.cc.bin.err.expected +++ b/samples/tests/outputs/long-messages.cc.bin.err.expected @@ -2,5 +2,5 @@ [----] A long message [----] Spawning multiple lines. [----] Formatting is respected. -[FAIL] sample::long_msg: (0.00s) +[FAIL] sample::long_msg [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/tests/outputs/tap_test.sh.err.expected b/samples/tests/outputs/tap_test.sh.err.expected new file mode 100644 index 00000000..56f3268c --- /dev/null +++ b/samples/tests/outputs/tap_test.sh.err.expected @@ -0,0 +1,61 @@ +TAP version 13 +1..2 +# Criterion v2.1.0 + +# Running 2 tests from misc +ok - misc::passing (0.00s) +not ok - misc::failing (0.00s) + simple.c:4: Assertion failed: The expression 0 is false. +TAP version 13 +1..3 +# Criterion v2.1.0 + +# Running 3 tests from simple +not ok - simple::wrong_signal (0.00s) +not ok - simple::uncaught unexpected signal after signal.c:16 +ok - simple::caught (0.00s) +TAP version 13 +1..6 +# Criterion v2.1.0 + +# Running 6 tests from asserts +ok - asserts::string (0.00s) +not ok - asserts::old_school (0.00s) + asserts.c:18: Assertion failed: The conditions for this assertion were not met. + asserts.c:17: Assertion failed: You can fail an assertion with a message from anywhere +ok - asserts::native (0.00s) +ok - asserts::float (0.00s) +not ok - asserts::base (0.00s) + asserts.c:12: Assertion failed: This assert runs + asserts.c:11: Assertion failed: assert is fatal, expect isn't +ok - asserts::array (0.00s) +TAP version 13 +1..3 +# Criterion v2.1.0 + +# Running 1 tests from suite2 +ok - suite2::test (0.00s) + +# Running 1 tests from suite1 +ok - suite1::test (0.00s) + +# Running 1 tests from disabled +ok - disabled::test # SKIP suite is disabled +TAP version 13 +1..1 +# Criterion v2.1.0 + +# Running 1 tests from sample +not ok - sample::long_msg (0.00s) + long-messages.c:4: Assertion failed: This is + A long message + Spawning multiple lines. + Formatting is respected. +TAP version 13 +1..2 +# Criterion v2.1.0 + +# Running 2 tests from misc +ok - misc::skipped This one is skipped # SKIP test is disabled +not ok - misc::failing Just a failing test (0.00s) + description.c:4: Assertion failed: The expression 0 is false. diff --git a/samples/tests/outputs/tap_test.sh.out.expected b/samples/tests/outputs/tap_test.sh.out.expected new file mode 100644 index 00000000..e69de29b diff --git a/samples/tests/outputs/theories_regression.c.bin.err.expected b/samples/tests/outputs/theories_regression.c.bin.err.expected new file mode 100644 index 00000000..1262f800 --- /dev/null +++ b/samples/tests/outputs/theories_regression.c.bin.err.expected @@ -0,0 +1,4 @@ +[----] theories_regression.c:34: Assertion failed: The conditions for this assertion were not met. +[----] Theory theory::misc failed with the following parameters: ('a', true, 1, 1, 3.14f, 3.14, "test", "other test") +[FAIL] theory::misc +[====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/tests/outputs/theories_regression.c.bin.out.expected b/samples/tests/outputs/theories_regression.c.bin.out.expected new file mode 100644 index 00000000..e69de29b diff --git a/samples/tests/outputs/theories_regression.cc.bin.err.expected b/samples/tests/outputs/theories_regression.cc.bin.err.expected new file mode 100644 index 00000000..e149d586 --- /dev/null +++ b/samples/tests/outputs/theories_regression.cc.bin.err.expected @@ -0,0 +1,4 @@ +[----] theories_regression.cc:36: Assertion failed: The conditions for this assertion were not met. +[----] Theory theory::misc failed with the following parameters: ('a', true, 1, 1, 3.14f, 3.14, "test", "other test") +[FAIL] theory::misc +[====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0  diff --git a/samples/tests/outputs/theories_regression.cc.bin.out.expected b/samples/tests/outputs/theories_regression.cc.bin.out.expected new file mode 100644 index 00000000..e69de29b diff --git a/samples/tests/outputs/xml_test.sh.err.expected b/samples/tests/outputs/xml_test.sh.err.expected new file mode 100644 index 00000000..b3f83625 --- /dev/null +++ b/samples/tests/outputs/xml_test.sh.err.expected @@ -0,0 +1,82 @@ + + + + + + + + simple.c:4: The expression 0 is false. + + + + + + + + + + + + + + + + + + + + + + + + asserts.c:18: The conditions for this assertion were not met. asserts.c:17: You can fail an assertion with a message from anywhere + + + + + + + asserts.c:12: This assert runs asserts.c:11: assert is fatal, expect isn't + + + + + + + + + + + + + + + + + + + + + + + + + + + + long-messages.c:4: This is A long message Spawning multiple lines. Formatting is respected. + + + + + + + + + + + + description.c:4: The expression 0 is false. + + + diff --git a/samples/tests/outputs/xml_test.sh.out.expected b/samples/tests/outputs/xml_test.sh.out.expected new file mode 100644 index 00000000..e69de29b diff --git a/samples/tests/run_test.sh b/samples/tests/run_test.sh index 8d81472d..14d386d0 100755 --- a/samples/tests/run_test.sh +++ b/samples/tests/run_test.sh @@ -13,14 +13,14 @@ else sh -c "$bin_dir/$*" > $out_dir/$1.out 2> $out_dir/$1.err fi -dos2unix $out_dir/$1.err $out_dir/$1.out - if [ -f $cmp_dir/$1.out.expected ] && [ "$(md5sum $out_dir/$1.out | cut -d' ' -f1)" != "$(md5sum $cmp_dir/$1.out.expected | cut -d' ' -f1)" ]; then - diff $out_dir/$1.out $cmp_dir/$1.out.expected - exit 255 + if ! diff --strip-trailing-cr $out_dir/$1.out $cmp_dir/$1.out.expected ; then + exit 255 + fi fi if [ -f $cmp_dir/$1.err.expected ] && [ "$(md5sum $out_dir/$1.err | cut -d' ' -f1)" != "$(md5sum $cmp_dir/$1.err.expected | cut -d' ' -f1)" ]; then - diff $out_dir/$1.err $cmp_dir/$1.err.expected - exit 255 + if ! diff --strip-trailing-cr $out_dir/$1.err $cmp_dir/$1.err.expected ; then + exit 255 + fi fi diff --git a/samples/tests/tap_test.sh b/samples/tests/tap_test.sh index 5e81fbf6..9faca2d2 100755 --- a/samples/tests/tap_test.sh +++ b/samples/tests/tap_test.sh @@ -3,5 +3,5 @@ ./signal.c.bin --tap --always-succeed ./asserts.c.bin --tap --always-succeed ./more-suites.c.bin --tap --always-succeed -./long-messages.c.bin --tap --always-succeed +./tests/long-messages.c.bin --tap --always-succeed ./description.c.bin --tap --always-succeed diff --git a/samples/tests/theories_regression.c b/samples/tests/theories_regression.c new file mode 100644 index 00000000..bcb953e5 --- /dev/null +++ b/samples/tests/theories_regression.c @@ -0,0 +1,35 @@ +#ifdef _MSC_VER +#pragma warning(disable : 4090) +#endif + +#include + +// Testing for various parameters + +TheoryDataPoints(theory, misc) = { + DataPoints(char, 'a'), + DataPoints(bool, true), + DataPoints(short, 1), + DataPoints(int, 1), + DataPoints(float, 3.14f), + DataPoints(double, 3.14), + DataPoints(char *, "test"), + DataPoints(const char *, "other test"), +}; + +Theory((char c, bool b, short s, int i, float f, double d, char *str, const char *cstr), theory, misc) { + float reff = 3.14f; + double refd = 3.14; + + cr_assert(b); + cr_assert_eq(c, 'a'); + cr_assert_eq(s, 1); + cr_assert_eq(i, 1); + cr_assert_eq(f, reff); + cr_assert_eq(d, refd); + cr_assert_str_eq(str, "test"); + cr_assert_str_eq(cstr, "other test"); + + // abort to see the formatted string of all parameters + cr_assert_fail(); +} diff --git a/samples/tests/theories_regression.cc b/samples/tests/theories_regression.cc new file mode 100644 index 00000000..75fdc0dc --- /dev/null +++ b/samples/tests/theories_regression.cc @@ -0,0 +1,37 @@ +#ifdef _MSC_VER +#pragma warning(disable : 4090) +#endif + +#include + +// Testing for various parameters + +char test_str[] = {'t', 'e', 's', 't', '\0'}; + +TheoryDataPoints(theory, misc) = { + DataPoints(char, 'a'), + DataPoints(bool, true), + DataPoints(short, 1), + DataPoints(int, 1), + DataPoints(float, 3.14f), + DataPoints(double, 3.14), + DataPoints(char *, test_str), + DataPoints(const char *, "other test"), +}; + +Theory((char c, bool b, short s, int i, float f, double d, char *str, const char *cstr), theory, misc) { + float reff = 3.14f; + double refd = 3.14; + + cr_assert(b); + cr_assert_eq(c, 'a'); + cr_assert_eq(s, 1); + cr_assert_eq(i, 1); + cr_assert_eq(f, reff); + cr_assert_eq(d, refd); + cr_assert_str_eq(str, "test"); + cr_assert_str_eq(cstr, "other test"); + + // abort to see the formatted string of all parameters + cr_assert_fail(); +} diff --git a/samples/tests/xml_test.sh b/samples/tests/xml_test.sh new file mode 100755 index 00000000..d0740d92 --- /dev/null +++ b/samples/tests/xml_test.sh @@ -0,0 +1,7 @@ +#!/bin/sh +./simple.c.bin --xml --always-succeed +./signal.c.bin --xml --always-succeed +./asserts.c.bin --xml --always-succeed +./more-suites.c.bin --xml --always-succeed +./tests/long-messages.c.bin --xml --always-succeed +./description.c.bin --xml --always-succeed diff --git a/samples/theories.c b/samples/theories.c index 0bbe7cc2..055811f7 100644 --- a/samples/theories.c +++ b/samples/theories.c @@ -87,14 +87,17 @@ TheoryDataPoints(theory, misc) = { }; Theory((char c, bool b, short s, int i, long l, long long ll, float f, double d, char *str, const char *cstr, struct my_object *obj), theory, misc) { + float reff = 3.14f; + double refd = 3.14; + cr_assert(b); cr_assert_eq(c, 'a'); cr_assert_eq(s, 1); cr_assert_eq(i, 1); cr_assert_eq(l, 1); cr_assert_eq(ll, 1); - cr_assert_eq(f, 3.14f); - cr_assert_eq(d, 3.14); + cr_assert_eq(f, reff); + cr_assert_eq(d, refd); cr_assert_str_eq(str, "test"); cr_assert_str_eq(cstr, "other test"); cr_assert_eq(obj->foo, 42); @@ -102,20 +105,3 @@ Theory((char c, bool b, short s, int i, long l, long long ll, float f, double d, // abort to see the formatted string of all parameters cr_assert_fail(); } - -// Manually generate datapoints - -TheoryDataPoints(theory, gen) = { - DataPoints(int, 0), // placeholder -}; - -static void generate_datapoints(void) { - static int arr[] = {1, 2, 3, 4, 5}; - TheoryDataPoint(theory, gen)[0].len = 5; - TheoryDataPoint(theory, gen)[0].arr = &arr; -} - -Theory((int i), theory, gen, .init = generate_datapoints) { - (void) i; - cr_assert_fail(); // we fail to display the parameter -} diff --git a/samples/theories.cc b/samples/theories.cc index 56027542..b79cb006 100644 --- a/samples/theories.cc +++ b/samples/theories.cc @@ -95,14 +95,17 @@ TheoryDataPoints(theory, misc) = { }; Theory((char c, bool b, short s, int i, long l, long long ll, float f, double d, char *str, const char *cstr, struct my_object *obj), theory, misc) { + float reff = 3.14f; + double refd = 3.14; + cr_assert(b); cr_assert_eq(c, 'a'); cr_assert_eq(s, 1); cr_assert_eq(i, 1); cr_assert_eq(l, 1); cr_assert_eq(ll, 1); - cr_assert_eq(f, 3.14f); - cr_assert_eq(d, 3.14); + cr_assert_eq(f, reff); + cr_assert_eq(d, refd); cr_assert_str_eq(str, "test"); cr_assert_str_eq(cstr, "other test"); cr_assert_eq(obj->foo, 42); @@ -110,20 +113,3 @@ Theory((char c, bool b, short s, int i, long l, long long ll, float f, double d, // abort to see the formatted string of all parameters cr_assert_fail(); } - -// Manually generate datapoints - -TheoryDataPoints(theory, gen) = { - DataPoints(int, 0), // placeholder -}; - -static void generate_datapoints(void) { - static int arr[] = {1, 2, 3, 4, 5}; - TheoryDataPoint(theory, gen)[0].len = 5; - TheoryDataPoint(theory, gen)[0].arr = &arr; -} - -Theory((int i), theory, gen, .init = generate_datapoints) { - (void) i; - cr_assert_fail(); // we fail to display the parameter -} diff --git a/src/compat/alloc.c b/src/compat/alloc.c index 7ae9d6d3..48d83f54 100644 --- a/src/compat/alloc.c +++ b/src/compat/alloc.c @@ -23,6 +23,7 @@ */ #include "alloc.h" #include "internal.h" +#include "criterion/logging.h" #include #ifdef VANILLA_WIN32 @@ -51,7 +52,7 @@ void init_inheritable_heap(void) { HeapDestroy(h->handle); if (g_heap == (HANDLE) NULL) { - fputs("Could not create the private inheritable heap.", stderr); + criterion_perror("Could not create the private inheritable heap.\n"); abort(); } } diff --git a/src/compat/internal.h b/src/compat/internal.h index 29b73ab7..deb41bc0 100644 --- a/src/compat/internal.h +++ b/src/compat/internal.h @@ -30,7 +30,22 @@ # undef _WIN32_WINNT # define _WIN32_WINNT 0x0502 # include + +# if defined(MINGW_DEFINE_OFF_T) && (defined(__MINGW32__) || defined(__MINGW64__)) +# include "off_t.h" + +# if !defined(__MINGW64__) +# define off_t cr_off32 +# else +# define off_t cr_off64 +# endif +# define off64_t cr_off64 +# endif # include +# if defined(MINGW_DEFINE_OFF_T) && (defined(__MINGW32__) || defined(__MINGW64__)) +# undef off_t +# undef off64_t +# endif # include # include # include @@ -40,6 +55,15 @@ # include # include # include +# include +# ifdef BSD +# include +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; +# include +# endif # endif # include "posix.h" diff --git a/src/compat/mockfile.c b/src/compat/mockfile.c index 4e2716d0..8f662b2e 100644 --- a/src/compat/mockfile.c +++ b/src/compat/mockfile.c @@ -31,7 +31,6 @@ #include "criterion/redirect.h" #ifdef __unix__ -#include # ifdef BSD typedef int cr_count; diff --git a/src/compat/off_t.h b/src/compat/off_t.h new file mode 100644 index 00000000..5918cea5 --- /dev/null +++ b/src/compat/off_t.h @@ -0,0 +1,32 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CR_OFF_T_DEFINED +# define CR_OFF_T_DEFINED + +# include + +typedef int32_t cr_off32; +typedef int64_t cr_off64; + +#endif /* !CR_OFF_T_DEFINED */ diff --git a/src/compat/pipe-internal.h b/src/compat/pipe-internal.h index 54a52422..17f114f3 100644 --- a/src/compat/pipe-internal.h +++ b/src/compat/pipe-internal.h @@ -35,4 +35,12 @@ struct pipe_handle { #endif }; +struct pipe_file_handle { +#ifdef VANILLA_WIN32 + HANDLE fh; +#else + int fd; +#endif +}; + #endif /* !PIPE_INTERNAL_H_ */ diff --git a/src/compat/pipe.c b/src/compat/pipe.c index 39167d97..1c05a201 100644 --- a/src/compat/pipe.c +++ b/src/compat/pipe.c @@ -182,6 +182,135 @@ void pipe_std_redirect(s_pipe_handle *pipe, enum criterion_std_fd fd) { #endif } +void close_pipe_file_handle(void *ptr, CR_UNUSED void *meta) { + s_pipe_file_handle *h = ptr; +#ifdef VANILLA_WIN32 + CloseHandle(h->fh); +#else + close(h->fd); +#endif +} + +#ifdef VANILLA_WIN32 +static HANDLE win_dup(HANDLE h) { + HANDLE dup; + DuplicateHandle(GetCurrentProcess(), + h, + GetCurrentProcess(), + &dup, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); + return dup; +} +#endif + +s_pipe_file_handle *pipe_out_handle(s_pipe_handle *p, enum pipe_opt opts) { +#ifdef VANILLA_WIN32 + if (opts & PIPE_CLOSE) + CloseHandle(p->fhs[0]); + HANDLE fh = p->fhs[1]; + if (opts & PIPE_DUP) + fh = win_dup(fh); + + s_pipe_file_handle *h = smalloc( + .size = sizeof (s_pipe_file_handle), + .dtor = close_pipe_file_handle); + + h->fh = fh; + return h; +#else + if (opts & PIPE_CLOSE) + close(p->fds[0]); + int fd = p->fds[1]; + if (opts & PIPE_DUP) + fd = dup(fd); + + s_pipe_file_handle *h = smalloc( + .size = sizeof (s_pipe_file_handle), + .dtor = close_pipe_file_handle); + + h->fd = fd; + return h; +#endif +} + +s_pipe_file_handle *pipe_in_handle(s_pipe_handle *p, enum pipe_opt opts) { +#ifdef VANILLA_WIN32 + if (opts & PIPE_CLOSE) + CloseHandle(p->fhs[1]); + HANDLE fh = p->fhs[0]; + if (opts & PIPE_DUP) + fh = win_dup(fh); + + s_pipe_file_handle *h = smalloc( + .size = sizeof (s_pipe_file_handle), + .dtor = close_pipe_file_handle); + + h->fh = fh; + return h; +#else + if (opts & PIPE_CLOSE) + close(p->fds[1]); + int fd = p->fds[0]; + if (opts & PIPE_DUP) + fd = dup(fd); + + s_pipe_file_handle *h = smalloc( + .size = sizeof (s_pipe_file_handle), + .dtor = close_pipe_file_handle); + + h->fd = fd; + return h; +#endif +} + +int pipe_write(const void *buf, size_t size, s_pipe_file_handle *pipe) { +#ifdef VANILLA_WIN32 + DWORD written = 0; + size_t off = 0; + while (size > 0) { + if (!WriteFile(pipe->fh, (const char *) buf + off, size, &written, NULL)) + return -1; + size -= written; + off += written; + } + if (off > 0) + return 1; + return 0; +#else + ssize_t res = write(pipe->fd, buf, size); + if (res < 0) + return -1; + if (res > 0) + return 1; + return 0; +#endif +} + +int pipe_read(void *buf, size_t size, s_pipe_file_handle *pipe) { +#ifdef VANILLA_WIN32 + DWORD read = 0; + size_t off = 0; + while (size > 0) { + if (!ReadFile(pipe->fh, (char *) buf + off, size, &read, NULL)) + return -1; + size -= read; + off += read; + } + if (off > 0) + return 1; + return 0; +#else + ssize_t res = read(pipe->fd, buf, size); + if (res < 0) + return -1; + if (res > 0) + return 1; + return 0; +#endif +} + static s_pipe_handle stdout_redir_; static s_pipe_handle stderr_redir_; static s_pipe_handle stdin_redir_; @@ -189,3 +318,25 @@ static s_pipe_handle stdin_redir_; s_pipe_handle *stdout_redir = &stdout_redir_; s_pipe_handle *stderr_redir = &stderr_redir_; s_pipe_handle *stdin_redir = &stdin_redir_; + +s_pipe_file_handle *pipe_file_open(const char *path) { + s_pipe_file_handle *h = smalloc( + .size = sizeof (s_pipe_file_handle), + .dtor = close_pipe_file_handle); +#ifdef VANILLA_WIN32 + if (!path) + path = "nul"; + h->fh = CreateFile(path, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); +#else + if (!path) + path = "/dev/null"; + h->fd = open(path, O_RDWR); +#endif + return h; +} diff --git a/src/compat/pipe.h b/src/compat/pipe.h index 83aa63db..67999826 100644 --- a/src/compat/pipe.h +++ b/src/compat/pipe.h @@ -25,11 +25,16 @@ # define PIPE_H_ # include +# include # include "common.h" +# include "criterion/logging.h" struct pipe_handle; typedef struct pipe_handle s_pipe_handle; +struct pipe_file_handle; +typedef struct pipe_file_handle s_pipe_file_handle; + enum pipe_end { PIPE_READ = 0, PIPE_WRITE = 1, @@ -42,6 +47,7 @@ enum criterion_std_fd { }; enum pipe_opt { + PIPE_NOOPT = 0, PIPE_DUP = 1 << 0, PIPE_CLOSE = 1 << 1, }; @@ -50,20 +56,29 @@ s_pipe_handle *stdpipe(); FILE *pipe_in(s_pipe_handle *p, enum pipe_opt opts); FILE *pipe_out(s_pipe_handle *p, enum pipe_opt opts); +s_pipe_file_handle *pipe_out_handle(s_pipe_handle *p, enum pipe_opt opts); +s_pipe_file_handle *pipe_in_handle(s_pipe_handle *p, enum pipe_opt opts); + int stdpipe_options(s_pipe_handle *pipe, int id, int noblock); void pipe_std_redirect(s_pipe_handle *pipe, enum criterion_std_fd fd); -INLINE FILE* get_std_file(int fd_kind) { +int pipe_write(const void *buf, size_t size, s_pipe_file_handle *pipe); +int pipe_read(void *buf, size_t size, s_pipe_file_handle *pipe); + +INLINE FILE* get_std_file(enum criterion_std_fd fd_kind) { switch (fd_kind) { case CR_STDIN: return stdin; case CR_STDOUT: return stdout; case CR_STDERR: return stderr; } - return NULL; + criterion_perror("get_std_file: invalid parameter.\n"); + abort(); } extern s_pipe_handle *stdout_redir; extern s_pipe_handle *stderr_redir; extern s_pipe_handle *stdin_redir; +s_pipe_file_handle *pipe_file_open(const char *path); + #endif /* !PIPE_H_ */ diff --git a/src/compat/posix.h b/src/compat/posix.h index 930225a6..1081942b 100644 --- a/src/compat/posix.h +++ b/src/compat/posix.h @@ -24,9 +24,9 @@ #ifndef POSIX_COMPAT_H_ # define POSIX_COMPAT_H_ -#if defined(_WIN32) && !defined(__CYGWIN__) -# define VANILLA_WIN32 -#endif +# if defined(_WIN32) && !defined(__CYGWIN__) +# define VANILLA_WIN32 +# endif # if defined(BSD) \ || defined(__FreeBSD__) \ @@ -40,7 +40,22 @@ # define _POSIX_SOURCE 1 # define TMP_POSIX # endif +# if defined(MINGW_DEFINE_OFF_T) && (defined(__MINGW32__) || defined(__MINGW64__)) +# include "off_t.h" + +# if !defined(__MINGW64__) +# define off_t cr_off32 +# else +# define off_t cr_off64 +# endif +# define off64_t cr_off64 +# endif # include +# include +# if defined(MINGW_DEFINE_OFF_T) && defined(__MINGW32__) || defined(__MINGW64__) +# undef off_t +# undef off64_t +# endif # ifdef TMP_POSIX # undef _POSIX_SOURCE # undef TMP_POSIX @@ -54,8 +69,8 @@ # define SIGPROF 27 # define CR_EXCEPTION_TIMEOUT 0xC0001042 + # else -# include # include # endif diff --git a/src/compat/process.c b/src/compat/process.c index 72320d25..6bb6872f 100644 --- a/src/compat/process.c +++ b/src/compat/process.c @@ -23,6 +23,7 @@ */ #include #include +#include #include #include "core/worker.h" #include "core/runner.h" @@ -87,6 +88,8 @@ static int get_win_status(HANDLE handle) { } return sig ? sig : exit_code << 8; } +#else +# include #endif struct worker_context g_worker_context = {.test = NULL}; @@ -97,7 +100,7 @@ struct full_context { struct criterion_test_extra_data test_data; struct criterion_suite suite; struct criterion_test_extra_data suite_data; - f_worker_func func; + cr_worker_func func; struct pipe_handle pipe; struct test_single_param param; HANDLE sync; @@ -114,9 +117,7 @@ static struct full_context local_ctx; # error Unsupported compiler. Use GCC or Clang under *nixes. # endif -static void handle_sigchld(UNUSED int sig) { - assert(sig == SIGCHLD); - +static void handle_sigchld_pump(void) { int fd = g_worker_pipe->fds[1]; pid_t pid; int status; @@ -126,15 +127,65 @@ static void handle_sigchld(UNUSED int sig) { (s_proc_handle) { pid }, get_status(status) }; - char buf[sizeof (int) + sizeof (struct worker_status)]; + unsigned long long pid_ull = (unsigned long long) pid; + + char buf[sizeof (int) + sizeof (pid_ull) + sizeof (struct worker_status)]; memcpy(buf, &kind, sizeof (kind)); - memcpy(buf + sizeof (kind), &ws, sizeof (ws)); + memcpy(buf + sizeof (kind), &pid_ull, sizeof (pid_ull)); + memcpy(buf + sizeof (kind) + sizeof (pid_ull), &ws, sizeof (ws)); - if (write(fd, &buf, sizeof (buf)) < (ssize_t) sizeof (buf)) + if (write(fd, &buf, sizeof (buf)) < (ssize_t) sizeof (buf)) { + criterion_perror("Could not write the WORKER_TERMINATED event " + "down the event pipe: %s.\n", + strerror(errno)); abort(); + } } } + +/* + * This child reaping logic is a dirty hack to prevent deadlocks + * when the pipe is overflowing and a child terminates. + * + * (Windows doesn't have this issue as the waitpid logic is threaded by + * RegisterWaitForSingleObject) + * + * REMOVE WHEN REFACTORING I/O LAYER + */ +static pthread_t child_pump; +static bool child_pump_running; + +static void *chld_pump_thread_main(void *nil) { + child_pump_running = true; + + do { + handle_sigchld_pump(); + usleep(1000); + } while (child_pump_running); + + return nil; +} +#endif + +void init_proc_compat(void) { +#ifndef VANILLA_WIN32 + pthread_attr_t attr; + int err = pthread_attr_init(&attr) + || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) + || pthread_create(&child_pump, &attr, chld_pump_thread_main, NULL) + || pthread_attr_destroy(&attr); + if (err) { + perror(0); + exit(1); + } +#endif +} + +void free_proc_compat(void) { +#ifndef VANILLA_WIN32 + child_pump_running = false; #endif +} #ifdef VANILLA_WIN32 struct wait_context { @@ -143,7 +194,7 @@ struct wait_context { }; static void CALLBACK handle_child_terminated(PVOID lpParameter, - UNUSED BOOLEAN TimerOrWaitFired) { + CR_UNUSED BOOLEAN TimerOrWaitFired) { assert(!TimerOrWaitFired); @@ -155,9 +206,12 @@ static void CALLBACK handle_child_terminated(PVOID lpParameter, (s_proc_handle) { wctx->proc_handle }, get_status(status) }; - char buf[sizeof (int) + sizeof (struct worker_status)]; + unsigned long long pid_ull = (unsigned long long) GetProcessId(wctx->proc_handle); + + char buf[sizeof (int) + sizeof (pid_ull) + sizeof (struct worker_status)]; memcpy(buf, &kind, sizeof (kind)); - memcpy(buf + sizeof (kind), &ws, sizeof (ws)); + memcpy(buf + sizeof (kind), &pid_ull, sizeof (pid_ull)); + memcpy(buf + sizeof (kind) + sizeof (pid_ull), &ws, sizeof (ws)); DWORD written; WriteFile(g_worker_pipe->fhs[1], buf, sizeof (buf), &written, NULL); @@ -240,16 +294,6 @@ int resume_child(void) { free(param); return 1; #else -# if defined(__unix__) || defined(__APPLE__) - struct sigaction sa; - sa.sa_handler = &handle_sigchld; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa, 0) == -1) { - perror(0); - exit(1); - } -# endif return 0; #endif } @@ -379,16 +423,6 @@ s_proc_handle *fork_process() { #endif } -void wait_process(s_proc_handle *handle, int *status) { -#ifdef VANILLA_WIN32 - WaitForSingleObject(handle->handle, INFINITE); - *status = get_win_status(handle->handle); - CloseHandle(handle->handle); -#else - waitpid(handle->pid, status, 0); -#endif -} - s_proc_handle *get_current_process() { s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); #ifdef VANILLA_WIN32 @@ -406,3 +440,19 @@ bool is_current_process(s_proc_handle *proc) { return proc->pid == getpid(); #endif } + +unsigned long long get_process_id(void) { +#ifdef VANILLA_WIN32 + return (unsigned long long) GetCurrentProcessId(); +#else + return (unsigned long long) getpid(); +#endif +} + +unsigned long long get_process_id_of(s_proc_handle *proc) { +#ifdef VANILLA_WIN32 + return (unsigned long long) GetProcessId(proc->handle); +#else + return (unsigned long long) proc->pid; +#endif +} diff --git a/src/compat/process.h b/src/compat/process.h index 0d707708..193c12b6 100644 --- a/src/compat/process.h +++ b/src/compat/process.h @@ -27,6 +27,8 @@ # include "criterion/types.h" # include "internal.h" +typedef void (*cr_worker_func)(struct criterion_test *, struct criterion_suite *); + struct proc_handle { #ifdef VANILLA_WIN32 HANDLE handle; @@ -40,7 +42,7 @@ typedef struct proc_handle s_proc_handle; struct worker_context { struct criterion_test *test; struct criterion_suite *suite; - f_worker_func func; + cr_worker_func func; struct pipe_handle *pipe; struct test_single_param *param; }; @@ -55,4 +57,10 @@ void wait_process(s_proc_handle *handle, int *status); s_proc_handle *get_current_process(); bool is_current_process(s_proc_handle *proc); +unsigned long long get_process_id(void); +unsigned long long get_process_id_of(s_proc_handle *proc); + +void init_proc_compat(void); +void free_proc_compat(void); + #endif /* !COMPAT_PROCESS_H_ */ diff --git a/src/compat/processor.c b/src/compat/processor.c new file mode 100644 index 00000000..605f57ea --- /dev/null +++ b/src/compat/processor.c @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "internal.h" + +size_t get_processor_count(void) { +#ifdef _WIN32 + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + return (size_t) sysinfo.dwNumberOfProcessors; +#elif defined(BSD) + int mib[2] = { CTL_HW, HW_NCPU }; +# ifdef __APPLE__ + size_t miblen = 2; + sysctlnametomib("hw.logicalcpu", mib, &miblen); +# endif + + long long count = 0; + size_t len = sizeof (count); + + int res = sysctl(mib, 2, &count, &len, NULL, 0); + + if (count < 1 || res == -1) + count = 1; + return (size_t) count; +#elif defined(__linux__) + return sysconf(_SC_NPROCESSORS_ONLN); +#else +# error System not supported +#endif +} diff --git a/src/compat/processor.h b/src/compat/processor.h new file mode 100644 index 00000000..5e6e35ba --- /dev/null +++ b/src/compat/processor.h @@ -0,0 +1,31 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef COMPAT_PROCESSOR_H_ +# define COMPAT_PROCESSOR_H_ + +# include + +size_t get_processor_count(void); + +#endif /* !COMPAT_PROCESSOR_H_ */ diff --git a/src/compat/section.h b/src/compat/section.h index 9f6359e6..c461ebfb 100644 --- a/src/compat/section.h +++ b/src/compat/section.h @@ -24,6 +24,8 @@ #ifndef SECTION_H_ # define SECTION_H_ +# include "criterion/internal/common.h" + # ifdef _WIN32 void *get_win_section_start(const char *section); void *get_win_section_end(const char *section); @@ -39,8 +41,8 @@ void *get_osx_section_end(const char *section); # define GET_SECTION_START(Name) get_osx_section_start(CR_STRINGIFY(Name)) # define GET_SECTION_END(Name) get_osx_section_end(CR_STRINGIFY(Name)) # else -# define GET_SECTION_START(Name) SECTION_START(Name) -# define GET_SECTION_END(Name) SECTION_END(Name) +# define GET_SECTION_START(Name) CR_SECTION_START(Name) +# define GET_SECTION_END(Name) CR_SECTION_END(Name) # endif #endif /* !SECTION_H_ */ diff --git a/src/compat/strtok.h b/src/compat/strtok.h new file mode 100644 index 00000000..6dbe96ce --- /dev/null +++ b/src/compat/strtok.h @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef COMPAT_STRTOK_H_ +# define COMPAT_STRTOK_H_ + +# include "config.h" + +# ifdef VANILLA_WIN32 +# if HAVE_STRTOK_S +# define strtok_r strtok_s +# else +static CR_INLINE char *strtok_r(char *str, const char *delim, CR_UNUSED char **saveptr) { + return strtok(str, delim); +} +# endif + +# ifdef _MSC_VER +# define strdup _strdup +# endif +# endif + +#endif /* !COMPAT_STRTOK_H_ */ diff --git a/src/compat/time.c b/src/compat/time.c index 14de484f..2254d50c 100644 --- a/src/compat/time.c +++ b/src/compat/time.c @@ -1,7 +1,8 @@ #include #include #include -#include "criterion/common.h" +#include "criterion/internal/common.h" +#include "criterion/options.h" #include "compat/time.h" #include "compat/posix.h" @@ -28,6 +29,9 @@ extern __attribute__ ((weak)) int clock_gettime(clockid_t, struct timespec *); #endif bool can_measure_time(void) { + if (!criterion_options.measure_time) + return false; + #if defined(__unix__) && !defined(__CYGWIN__) return clock_gettime != NULL; #else @@ -128,11 +132,6 @@ int setup_timeout(uint64_t nanos) { CloseHandle(thread); return 0; #elif defined(__unix__) - if (!can_measure_time()) { - errno = ENOTSUP; - return -1; - } - timer_t timer; int res = timer_create(CLOCK_MONOTONIC, &(struct sigevent) { .sigev_notify = SIGEV_SIGNAL, diff --git a/src/compat/time.h b/src/compat/time.h index 14f301d9..427a137c 100644 --- a/src/compat/time.h +++ b/src/compat/time.h @@ -1,3 +1,26 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #ifndef TIMER_H_ # define TIMER_H_ diff --git a/src/config.h.in b/src/config.h.in index 5928b0bd..0040dad9 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -3,6 +3,9 @@ #cmakedefine ENABLE_NLS @ENABLE_NLS@ #cmakedefine HAVE_PCRE @HAVE_PCRE@ +#cmakedefine ENABLE_VALGRIND_ERRORS @ENABLE_VALGRIND_ERRORS@ +#cmakedefine MINGW_DEFINE_OFF_T @MINGW_DEFINE_OFF_T@ +#cmakedefine01 HAVE_STRTOK_S # define LOCALEDIR "${LOCALEDIR}" # define PACKAGE "${PROJECT_NAME}" diff --git a/src/core/abort.c b/src/core/abort.c index 1124244c..3fc35185 100644 --- a/src/core/abort.c +++ b/src/core/abort.c @@ -21,10 +21,34 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include "abort.h" +#include "criterion/internal/asprintf-compat.h" +#include "io/event.h" jmp_buf g_pre_test; void criterion_abort_test(void) { longjmp(g_pre_test, 1); } + +void criterion_test_die(const char *msg, ...) { + va_list vl; + va_start(vl, msg); + char *formatted_msg = NULL; + int res = cr_vasprintf(&formatted_msg, msg, vl); + va_end(vl); + + if (res < 0) + abort(); + + size_t *buf = malloc(sizeof (size_t) + res + 1); + *buf = res + 1; + memcpy(buf + 1, formatted_msg, res + 1); + + criterion_send_event(TEST_ABORT, buf, sizeof(size_t) + res + 1); + free(buf); + free(formatted_msg); + + exit(0); +} diff --git a/src/core/coroutine.h b/src/core/coroutine.h new file mode 100644 index 00000000..badea854 --- /dev/null +++ b/src/core/coroutine.h @@ -0,0 +1,188 @@ +/* coroutine.h + * + * Coroutine mechanics, implemented on top of standard ANSI C. See + * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for + * a full discussion of the theory behind this. + * + * To use these macros to define a coroutine, you need to write a + * function that looks something like this. + * + * [Simple version using static variables (scr macros)] + * int ascending (void) { + * static int i; + * + * scrBegin; + * for (i=0; i<10; i++) { + * scrReturn(i); + * } + * scrFinish(-1); + * } + * + * [Re-entrant version using an explicit context structure (ccr macros)] + * int ascending (ccrContParam) { + * ccrBeginContext; + * int i; + * ccrEndContext(foo); + * + * ccrBegin(foo); + * for (foo->i=0; foo->i<10; foo->i++) { + * ccrReturn(foo->i); + * } + * ccrFinish(-1); + * } + * + * In the static version, you need only surround the function body + * with `scrBegin' and `scrFinish', and then you can do `scrReturn' + * within the function and on the next call control will resume + * just after the scrReturn statement. Any local variables you need + * to be persistent across an `scrReturn' must be declared static. + * + * In the re-entrant version, you need to declare your persistent + * variables between `ccrBeginContext' and `ccrEndContext'. These + * will be members of a structure whose name you specify in the + * parameter to `ccrEndContext'. + * + * The re-entrant macros will malloc() the state structure on first + * call, and free() it when `ccrFinish' is reached. If you want to + * abort in the middle, you can use `ccrStop' to free the state + * structure immediately (equivalent to an explicit return() in a + * caller-type routine). + * + * A coroutine returning void type may call `ccrReturnV', + * `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to + * specify an empty parameter to the ordinary return macros. + * + * Ground rules: + * - never put `ccrReturn' or `scrReturn' within an explicit `switch'. + * - never put two `ccrReturn' or `scrReturn' statements on the same + * source line. + * + * The caller of a static coroutine calls it just as if it were an + * ordinary function: + * + * void main(void) { + * int i; + * do { + * i = ascending(); + * printf("got number %d\n", i); + * } while (i != -1); + * } + * + * The caller of a re-entrant coroutine must provide a context + * variable: + * + * void main(void) { + * ccrContext z = 0; + * do { + * printf("got number %d\n", ascending (&z)); + * } while (z); + * } + * + * Note that the context variable is set back to zero when the + * coroutine terminates (by crStop, or by control reaching + * crFinish). This can make the re-entrant coroutines more useful + * than the static ones, because you can tell when they have + * finished. + * + * If you need to dispose of a crContext when it is non-zero (that + * is, if you want to stop calling a coroutine without suffering a + * memory leak), the caller should call `ccrAbort(ctx)' where `ctx' + * is the context variable. + * + * This mechanism could have been better implemented using GNU C + * and its ability to store pointers to labels, but sadly this is + * not part of the ANSI C standard and so the mechanism is done by + * case statements instead. That's why you can't put a crReturn() + * inside a switch() statement. + */ + +/* + * coroutine.h is copyright 1995,2000 Simon Tatham. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef COROUTINE_H +#define COROUTINE_H + +#include + +/* + * `scr' macros for static coroutines. + */ + +#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:; +#define scrFinish(z) } return (z) +#define scrFinishV } return + +#define scrReturn(z) \ + do {\ + scrLine=__LINE__;\ + return (z); case __LINE__:;\ + } while (0) +#define scrReturnV \ + do {\ + scrLine=__LINE__;\ + return; case __LINE__:;\ + } while (0) + +/* + * `ccr' macros for re-entrant coroutines. + */ + +#define ccrContParam void **ccrParam + +#define ccrBeginContext typedef struct { int ccrLine +#define ccrEndContext(x) } ccrContextTag; ccrContextTag *x = (ccrContextTag *)*ccrParam + +#define ccrUseNamedContext(Name, x) \ + typedef struct Name ccrContextTag; \ + ccrContextTag *x = (ccrContextTag *)*ccrParam + +#define ccrBeginDefineContextType(Name) struct Name { int ccrLine +#define ccrEndDefineContextType } + +#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\ + if (x) switch(x->ccrLine) { case 0:; +#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z) +#define ccrFinishV } free(*ccrParam); *ccrParam=0; return + +#define ccrReturn(z) \ + do {\ + ((ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\ + return (z); case __LINE__:;\ + } while (0) +#define ccrReturnV \ + do {\ + ((ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\ + return; case __LINE__:;\ + } while (0) + +#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0) +#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0) + +#define ccrContext void * +#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0) + +#endif /* COROUTINE_H */ diff --git a/src/core/ordered-set.c b/src/core/ordered-set.c index 00630238..dde5d3cb 100644 --- a/src/core/ordered-set.c +++ b/src/core/ordered-set.c @@ -22,21 +22,21 @@ * THE SOFTWARE. */ #include -#include -#include +#include +#include #include #include "common.h" -static void destroy_ordered_set(void *ptr, UNUSED void *meta) { +static void destroy_ordered_set(void *ptr, CR_UNUSED void *meta) { sfree(((struct criterion_ordered_set *) ptr)->first); } -static INLINE void nothing(UNUSED void *ptr, UNUSED void *meta) {} +static INLINE void nothing(CR_UNUSED void *ptr, CR_UNUSED void *meta) {} static void destroy_ordered_set_node(void *ptr, void *meta) { struct criterion_ordered_set *set = *(void **) meta; struct criterion_ordered_set_node *n = ptr; - DEF(set->dtor, nothing)(n->data, NULL); + DEF(set->dtor, nothing)(n + 1, NULL); sfree(((struct criterion_ordered_set_node *) ptr)->next); } @@ -58,11 +58,11 @@ void *insert_ordered_set(struct criterion_ordered_set *l, size_t size) { int cmp; struct criterion_ordered_set_node *n, *prev = NULL; - for (n = l->first; n && (cmp = l->cmp(ptr, n->data)) > 0; n = n->next) + for (n = l->first; n && (cmp = l->cmp(ptr, n + 1)) > 0; n = n->next) prev = n; if (n && !cmp) // element already exists - return n->data; + return n + 1; struct criterion_ordered_set_node *new = smalloc( .size = sizeof(struct criterion_ordered_set_node) + size, @@ -72,7 +72,7 @@ void *insert_ordered_set(struct criterion_ordered_set *l, if (!new) return NULL; - memcpy(new->data, ptr, size); + memcpy(new + 1, ptr, size); new->next = n; if (prev) { prev->next = new; @@ -81,5 +81,5 @@ void *insert_ordered_set(struct criterion_ordered_set *l, } ++l->size; - return new->data; + return new + 1; } diff --git a/src/core/report.c b/src/core/report.c index 025d4849..f6c2398b 100644 --- a/src/core/report.c +++ b/src/core/report.c @@ -28,7 +28,7 @@ #include "criterion/stats.h" #include "criterion/logging.h" #include "criterion/options.h" -#include "criterion/ordered-set.h" +#include "criterion/internal/ordered-set.h" #include "report.h" #include "config.h" #include "compat/posix.h" @@ -36,62 +36,62 @@ static inline void nothing() {} #define IMPL_CALL_REPORT_HOOKS(Kind) \ - IMPL_SECTION_LIMITS(f_report_hook, HOOK_SECTION(Kind)); \ + CR_IMPL_SECTION_LIMITS(f_report_hook, CR_HOOK_SECTION(Kind)); \ void call_report_hooks_##Kind(void *data) { \ - for (f_report_hook *hook = GET_SECTION_START(HOOK_SECTION(Kind)); \ - hook < (f_report_hook*) GET_SECTION_END(HOOK_SECTION(Kind)); \ + for (f_report_hook *hook = GET_SECTION_START(CR_HOOK_SECTION(Kind)); \ + hook < (f_report_hook*) GET_SECTION_END(CR_HOOK_SECTION(Kind)); \ ++hook) { \ (*hook ? *hook : nothing)(data); \ } \ } #ifdef _MSC_VER -f_report_hook SECTION_START_(HOOK_SECTION(PRE_ALL)); -f_report_hook SECTION_START_(HOOK_SECTION(PRE_SUITE)); -f_report_hook SECTION_START_(HOOK_SECTION(PRE_INIT)); -f_report_hook SECTION_START_(HOOK_SECTION(PRE_TEST)); -f_report_hook SECTION_START_(HOOK_SECTION(ASSERT)); -f_report_hook SECTION_START_(HOOK_SECTION(THEORY_FAIL)); -f_report_hook SECTION_START_(HOOK_SECTION(TEST_CRASH)); -f_report_hook SECTION_START_(HOOK_SECTION(POST_TEST)); -f_report_hook SECTION_START_(HOOK_SECTION(POST_FINI)); -f_report_hook SECTION_START_(HOOK_SECTION(POST_SUITE)); -f_report_hook SECTION_START_(HOOK_SECTION(POST_ALL)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(PRE_ALL)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(PRE_SUITE)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(PRE_INIT)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(PRE_TEST)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(ASSERT)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(THEORY_FAIL)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(TEST_CRASH)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(POST_TEST)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(POST_FINI)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(POST_SUITE)); +f_report_hook CR_SECTION_START_(CR_HOOK_SECTION(POST_ALL)); -f_report_hook SECTION_END_(HOOK_SECTION(PRE_ALL)); -f_report_hook SECTION_END_(HOOK_SECTION(PRE_SUITE)); -f_report_hook SECTION_END_(HOOK_SECTION(PRE_INIT)); -f_report_hook SECTION_END_(HOOK_SECTION(PRE_TEST)); -f_report_hook SECTION_END_(HOOK_SECTION(ASSERT)); -f_report_hook SECTION_END_(HOOK_SECTION(THEORY_FAIL)); -f_report_hook SECTION_END_(HOOK_SECTION(TEST_CRASH)); -f_report_hook SECTION_END_(HOOK_SECTION(POST_TEST)); -f_report_hook SECTION_END_(HOOK_SECTION(POST_FINI)); -f_report_hook SECTION_END_(HOOK_SECTION(POST_SUITE)); -f_report_hook SECTION_END_(HOOK_SECTION(POST_ALL)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(PRE_ALL)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(PRE_SUITE)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(PRE_INIT)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(PRE_TEST)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(ASSERT)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(THEORY_FAIL)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(TEST_CRASH)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(POST_TEST)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(POST_FINI)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(POST_SUITE)); +f_report_hook CR_SECTION_END_(CR_HOOK_SECTION(POST_ALL)); #endif -IMPL_CALL_REPORT_HOOKS(PRE_ALL); -IMPL_CALL_REPORT_HOOKS(PRE_SUITE); -IMPL_CALL_REPORT_HOOKS(PRE_INIT); -IMPL_CALL_REPORT_HOOKS(PRE_TEST); -IMPL_CALL_REPORT_HOOKS(ASSERT); -IMPL_CALL_REPORT_HOOKS(THEORY_FAIL); -IMPL_CALL_REPORT_HOOKS(TEST_CRASH); -IMPL_CALL_REPORT_HOOKS(POST_TEST); -IMPL_CALL_REPORT_HOOKS(POST_FINI); -IMPL_CALL_REPORT_HOOKS(POST_SUITE); -IMPL_CALL_REPORT_HOOKS(POST_ALL); +IMPL_CALL_REPORT_HOOKS(PRE_ALL) +IMPL_CALL_REPORT_HOOKS(PRE_SUITE) +IMPL_CALL_REPORT_HOOKS(PRE_INIT) +IMPL_CALL_REPORT_HOOKS(PRE_TEST) +IMPL_CALL_REPORT_HOOKS(ASSERT) +IMPL_CALL_REPORT_HOOKS(THEORY_FAIL) +IMPL_CALL_REPORT_HOOKS(TEST_CRASH) +IMPL_CALL_REPORT_HOOKS(POST_TEST) +IMPL_CALL_REPORT_HOOKS(POST_FINI) +IMPL_CALL_REPORT_HOOKS(POST_SUITE) +IMPL_CALL_REPORT_HOOKS(POST_ALL) -ReportHook(PRE_ALL)(UNUSED struct criterion_test_set *arg) {} -ReportHook(PRE_SUITE)(UNUSED struct criterion_suite_set *arg) {} -ReportHook(PRE_INIT)(UNUSED struct criterion_test *arg) {} -ReportHook(PRE_TEST)(UNUSED struct criterion_test *arg) {} -ReportHook(ASSERT)(UNUSED struct criterion_assert_stats *arg) {} -ReportHook(THEORY_FAIL)(UNUSED struct criterion_theory_stats *arg) {} -ReportHook(TEST_CRASH)(UNUSED struct criterion_test_stats *arg) {} -ReportHook(POST_TEST)(UNUSED struct criterion_test_stats *arg) {} -ReportHook(POST_FINI)(UNUSED struct criterion_test_stats *arg) {} -ReportHook(POST_SUITE)(UNUSED struct criterion_suite_stats *arg) {} -ReportHook(POST_ALL)(UNUSED struct criterion_global_stats *arg) {} +ReportHook(PRE_ALL)(CR_UNUSED struct criterion_test_set *arg) {} +ReportHook(PRE_SUITE)(CR_UNUSED struct criterion_suite_set *arg) {} +ReportHook(PRE_INIT)(CR_UNUSED struct criterion_test *arg) {} +ReportHook(PRE_TEST)(CR_UNUSED struct criterion_test *arg) {} +ReportHook(ASSERT)(CR_UNUSED struct criterion_assert_stats *arg) {} +ReportHook(THEORY_FAIL)(CR_UNUSED struct criterion_theory_stats *arg) {} +ReportHook(TEST_CRASH)(CR_UNUSED struct criterion_test_stats *arg) {} +ReportHook(POST_TEST)(CR_UNUSED struct criterion_test_stats *arg) {} +ReportHook(POST_FINI)(CR_UNUSED struct criterion_test_stats *arg) {} +ReportHook(POST_SUITE)(CR_UNUSED struct criterion_suite_stats *arg) {} +ReportHook(POST_ALL)(CR_UNUSED struct criterion_global_stats *arg) {} diff --git a/src/core/report.h b/src/core/report.h index bc2d166e..27139741 100644 --- a/src/core/report.h +++ b/src/core/report.h @@ -25,11 +25,13 @@ # define REPORT_H_ # include "criterion/hooks.h" +# include "criterion/options.h" -# define report(Kind, Data) call_report_hooks_##Kind(Data) +# define report(Kind, Data) report_(Kind, Data) +# define report_(Kind, Data) call_report_hooks_##Kind(Data) # define DECL_CALL_REPORT_HOOKS(Kind) \ - DECL_SECTION_LIMITS(f_report_hook, HOOK_SECTION(Kind)); \ + CR_DECL_SECTION_LIMITS(f_report_hook, CR_HOOK_SECTION(Kind)); \ void call_report_hooks_##Kind(void *data) DECL_CALL_REPORT_HOOKS(PRE_ALL); @@ -44,9 +46,9 @@ DECL_CALL_REPORT_HOOKS(POST_FINI); DECL_CALL_REPORT_HOOKS(POST_SUITE); DECL_CALL_REPORT_HOOKS(POST_ALL); -#define log(Type, Arg) \ - log_(criterion_options.output_provider->log_ ## Type, Arg); -#define log_(Log, Arg) \ - (Log ? Log(Arg) : nothing()); +#define log(Type, ...) \ + log_(criterion_options.logger->log_ ## Type, __VA_ARGS__); +#define log_(Log, ...) \ + (Log ? Log(__VA_ARGS__) : nothing()); #endif /* !REPORT_H_ */ diff --git a/src/core/runner.c b/src/core/runner.c index a998843c..9078a6af 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -21,17 +21,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#define CRITERION_LOGGING_COLORS #include #include +#include #include -#include "criterion/criterion.h" +#include +#include "criterion/internal/test.h" #include "criterion/options.h" -#include "criterion/ordered-set.h" +#include "criterion/internal/ordered-set.h" #include "criterion/logging.h" +#include "criterion/internal/preprocess.h" #include "compat/time.h" #include "compat/posix.h" +#include "compat/processor.h" #include "string/i18n.h" #include "io/event.h" +#include "io/output.h" +#include "runner_coroutine.h" #include "stats.h" #include "runner.h" #include "report.h" @@ -44,19 +51,40 @@ #include "string/extmatch.h" #endif +typedef const char *const msg_t; + +#ifdef ENABLE_NLS +static msg_t msg_valgrind_early_exit = N_("%1$sWarning! Criterion has detected " + "that it is running under valgrind, but the no_early_exit option is " + "explicitely disabled. Reports will not be accurate!%2$s\n"); + +static msg_t msg_valgrind_jobs = N_("%1$sWarning! Criterion has detected " + "that it is running under valgrind, but the number of jobs have been " + "explicitely set. Reports might appear confusing!%2$s\n"); +#else +static msg_t msg_valgrind_early_exit = "%sWarning! Criterion has detected " + "that it is running under valgrind, but the no_early_exit option is " + "explicitely disabled. Reports will not be accurate!%s\n"; + +static msg_t msg_valgrind_jobs = "%sWarning! Criterion has detected " + "that it is running under valgrind, but the number of jobs have been " + "explicitely set. Reports might appear confusing!%s\n"; +#endif + + #ifdef _MSC_VER -struct criterion_test *SECTION_START_(cr_tst); -struct criterion_suite *SECTION_START_(cr_sts); -struct criterion_test *SECTION_END_(cr_tst); -struct criterion_suite *SECTION_END_(cr_sts); +struct criterion_test *CR_SECTION_START_(cr_tst); +struct criterion_suite *CR_SECTION_START_(cr_sts); +struct criterion_test *CR_SECTION_END_(cr_tst); +struct criterion_suite *CR_SECTION_END_(cr_sts); #endif -IMPL_SECTION_LIMITS(struct criterion_test*, cr_tst); -IMPL_SECTION_LIMITS(struct criterion_suite*, cr_sts); +CR_IMPL_SECTION_LIMITS(struct criterion_test*, cr_tst); +CR_IMPL_SECTION_LIMITS(struct criterion_suite*, cr_sts); // This is here to make the test suite & test sections non-empty -TestSuite(); -Test(,) {}; +CR_SECTION_("cr_sts") struct criterion_suite *dummy_suite = NULL; +CR_SECTION_("cr_tst") struct criterion_test *dummy_test = NULL; static INLINE void nothing(void) {} @@ -70,12 +98,12 @@ int cmp_test(void *a, void *b) { return strcmp(s1->name, s2->name); } -static void dtor_suite_set(void *ptr, UNUSED void *meta) { +static void dtor_suite_set(void *ptr, CR_UNUSED void *meta) { struct criterion_suite_set *s = ptr; sfree(s->tests); } -static void dtor_test_set(void *ptr, UNUSED void *meta) { +static void dtor_test_set(void *ptr, CR_UNUSED void *meta) { struct criterion_test_set *t = ptr; sfree(t->suites); } @@ -98,7 +126,7 @@ struct criterion_test_set *criterion_init(void) { struct criterion_ordered_set *suites = new_ordered_set(cmp_suite, dtor_suite_set); FOREACH_SUITE_SEC(s) { - if (!*s) + if (!*s || !*(*s)->name) continue; struct criterion_suite_set css = { @@ -121,7 +149,7 @@ struct criterion_test_set *criterion_init(void) { if (!*test) continue; - if (!(*test)->category) + if (!*(*test)->category || !*(*test)->name) continue; criterion_register_test(set, *test); @@ -130,111 +158,42 @@ struct criterion_test_set *criterion_init(void) { return set; } -typedef void (*f_test_run)(struct criterion_global_stats *, - struct criterion_suite_stats *, - struct criterion_test *, - struct criterion_suite *); - -static void map_tests(struct criterion_test_set *set, - struct criterion_global_stats *stats, - f_test_run fun) { - - FOREACH_SET(struct criterion_suite_set *s, set->suites) { - if (!s->tests) - continue; - - report(PRE_SUITE, s); - log(pre_suite, s); - - struct criterion_suite_stats *suite_stats = suite_stats_init(&s->suite); - - struct event ev = { .kind = PRE_SUITE }; - stat_push_event(stats, suite_stats, NULL, &ev); - - FOREACH_SET(struct criterion_test *t, s->tests) { - fun(stats, suite_stats, t, &s->suite); - if (criterion_options.fail_fast && stats->tests_failed > 0) - break; - if (!is_runner()) { - sfree(suite_stats); - return; - } - } - - report(POST_SUITE, suite_stats); - log(post_suite, suite_stats); +const struct criterion_test *criterion_current_test; +const struct criterion_suite *criterion_current_suite; - sfree(suite_stats); - } +void run_test_child(struct criterion_test *test, + struct criterion_suite *suite) { -} +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_ENABLE_ERROR_REPORTING; +#endif -static void run_test_child(struct criterion_test *test, - struct criterion_suite *suite) { + criterion_current_test = test; + criterion_current_suite = suite; if (suite->data && suite->data->timeout != 0 && test->data->timeout == 0) setup_timeout((uint64_t) (suite->data->timeout * 1e9)); else if (test->data->timeout != 0) setup_timeout((uint64_t) (test->data->timeout * 1e9)); - send_event(PRE_INIT, NULL, 0); - if (suite->data) - (suite->data->init ? suite->data->init : nothing)(); - (test->data->init ? test->data->init : nothing)(); - send_event(PRE_TEST, NULL, 0); - - struct timespec_compat ts; - if (!setjmp(g_pre_test)) { - timer_start(&ts); - if (test->test) { - if (!test->data->param_) { - test->test(); - } else { - void(*param_test_func)(void *) = (void(*)(void*)) test->test; - param_test_func(g_worker_context.param->ptr); - } - } - } - - double elapsed_time; - if (!timer_end(&elapsed_time, &ts)) - elapsed_time = -1; - - send_event(POST_TEST, &elapsed_time, sizeof (double)); - (test->data->fini ? test->data->fini : nothing)(); - if (suite->data) - (suite->data->fini ? suite->data->fini : nothing)(); - send_event(POST_FINI, NULL, 0); -} - -static INLINE bool is_disabled(struct criterion_test *t, - struct criterion_suite *s) { - - return t->data->disabled || (s->data && s->data->disabled); + if (test->test) + test->test(); } -#define push_event(Kind, ...) \ +#define push_event(...) \ do { \ stat_push_event(ctx->stats, \ ctx->suite_stats, \ ctx->test_stats, \ - &(struct event) { .kind = Kind, __VA_ARGS__ }); \ - report(Kind, ctx->test_stats); \ + &(struct event) { \ + .kind = CR_VA_HEAD(__VA_ARGS__), \ + CR_VA_TAIL(__VA_ARGS__) \ + }); \ + report(CR_VA_HEAD(__VA_ARGS__), ctx->test_stats); \ } while (0) s_pipe_handle *g_worker_pipe; -struct execution_context { - bool test_started; - bool normal_finish; - bool cleaned_up; - struct criterion_global_stats *stats; - struct criterion_test *test; - struct criterion_test_stats *test_stats; - struct criterion_suite *suite; - struct criterion_suite_stats *suite_stats; -}; - static void handle_worker_terminated(struct event *ev, struct execution_context *ctx) { @@ -275,6 +234,18 @@ static void handle_worker_terminated(struct event *ev, log(post_fini, ctx->test_stats); } } else { + if (ctx->aborted) { + if (!ctx->normal_finish) { + double elapsed_time = 0; + push_event(POST_TEST, .data = &elapsed_time); + log(post_test, ctx->test_stats); + } + if (!ctx->cleaned_up) { + push_event(POST_FINI); + log(post_fini, ctx->test_stats); + } + return; + } if ((ctx->normal_finish && !ctx->cleaned_up) || !ctx->test_started) { log(abnormal_exit, ctx->test_stats); if (!ctx->test_started) { @@ -301,119 +272,50 @@ static void handle_worker_terminated(struct event *ev, } } -static void run_test(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test *test, - struct criterion_suite *suite, - struct test_single_param *param) { - - struct criterion_test_stats *test_stats = test_stats_init(test); - struct process *proc = NULL; - - if (is_disabled(test, suite)) { - stat_push_event(stats, - suite_stats, - test_stats, - &(struct event) { .kind = PRE_INIT }); - goto cleanup; - } - - proc = spawn_test_worker(test, suite, run_test_child, g_worker_pipe, param); - if (proc == NULL && !is_runner()) - goto cleanup; - - struct execution_context ctx = { - .stats = stats, - .test = test, - .test_stats = test_stats, - .suite = suite, - .suite_stats = suite_stats, - }; - struct event *ev; - while ((ev = worker_read_event(proc)) != NULL) { - if (ev->kind < WORKER_TERMINATED) - stat_push_event(stats, suite_stats, test_stats, ev); - switch (ev->kind) { - case PRE_INIT: - report(PRE_INIT, test); - log(pre_init, test); - break; - case PRE_TEST: - report(PRE_TEST, test); - log(pre_test, test); - ctx.test_started = true; - break; - case THEORY_FAIL: { - struct criterion_theory_stats ths = { - .formatted_args = (char*) ev->data, - .stats = test_stats, - }; - report(THEORY_FAIL, &ths); - log(theory_fail, &ths); - } break; - case ASSERT: - report(ASSERT, ev->data); - log(assert, ev->data); - break; - case POST_TEST: - report(POST_TEST, test_stats); - log(post_test, test_stats); - ctx.normal_finish = true; - break; - case POST_FINI: - report(POST_FINI, test_stats); - log(post_fini, test_stats); - ctx.cleaned_up = true; - break; - case WORKER_TERMINATED: - handle_worker_terminated(ev, &ctx); - sfree(ev); - goto cleanup; - } - sfree(ev); - } - -cleanup: - sfree(test_stats); - sfree(proc); -} - -static void run_test_param(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test *test, - struct criterion_suite *suite) { - - if (!test->data->param_) - return; - - struct criterion_test_params params = test->data->param_(); - for (size_t i = 0; i < params.length; ++i) { - struct test_single_param param = { params.size, (char *) params.params + i * params.size }; - - run_test(stats, suite_stats, test, suite, ¶m); - if (criterion_options.fail_fast && stats->tests_failed > 0) +static void handle_event(struct event *ev) { + struct execution_context *ctx = &ev->worker->ctx; + if (ev->kind < WORKER_TERMINATED) + stat_push_event(ctx->stats, ctx->suite_stats, ctx->test_stats, ev); + switch (ev->kind) { + case PRE_INIT: + report(PRE_INIT, ctx->test); + log(pre_init, ctx->test); break; - if (!is_runner()) + case PRE_TEST: + report(PRE_TEST, ctx->test); + log(pre_test, ctx->test); + ctx->test_started = true; break; - } - - if (params.cleanup) - params.cleanup(¶ms); -} - -static void run_test_switch(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test *test, - struct criterion_suite *suite) { - - switch (test->data->kind_) { - case CR_TEST_NORMAL: - run_test(stats, suite_stats, test, suite, NULL); + case THEORY_FAIL: { + struct criterion_theory_stats ths = { + .formatted_args = (char*) ev->data, + .stats = ctx->test_stats, + }; + report(THEORY_FAIL, &ths); + log(theory_fail, &ths); + } break; + case ASSERT: + report(ASSERT, ev->data); + log(assert, ev->data); + break; + case TEST_ABORT: + log(test_abort, ctx->test_stats, ev->data); + ctx->test_stats->failed = 1; + ctx->aborted = true; break; - case CR_TEST_PARAMETERIZED: - run_test_param(stats, suite_stats, test, suite); + case POST_TEST: + report(POST_TEST, ctx->test_stats); + log(post_test, ctx->test_stats); + ctx->normal_finish = true; + break; + case POST_FINI: + report(POST_FINI, ctx->test_stats); + log(post_fini, ctx->test_stats); + ctx->cleaned_up = true; + break; + case WORKER_TERMINATED: + handle_worker_terminated(ev, ctx); break; - default: break; } } @@ -440,28 +342,119 @@ void disable_unmatching(struct criterion_test_set *set) { struct criterion_test_set *criterion_initialize(void) { init_i18n(); +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + if (RUNNING_ON_VALGRIND) { + criterion_options.no_early_exit = 1; + criterion_options.jobs = 1; + } + if (resume_child()) // (windows only) resume from the fork exit(0); + criterion_register_output_provider("tap", tap_report); + criterion_register_output_provider("xml", xml_report); + criterion_register_output_provider("json", json_report); + return criterion_init(); } void criterion_finalize(struct criterion_test_set *set) { sfree(set); + +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + + criterion_free_output(); +} + +static void run_tests_async(struct criterion_test_set *set, + struct criterion_global_stats *stats) { + + ccrContext ctx = 0; + + size_t nb_workers = DEF(criterion_options.jobs, get_processor_count()); + struct worker_set workers = { + .max_workers = nb_workers, + .workers = calloc(nb_workers, sizeof (struct worker*)), + }; + + size_t active_workers = 0; + + s_pipe_file_handle *event_pipe = pipe_in_handle(g_worker_pipe, PIPE_DUP); + struct event *ev = NULL; + + // initialization of coroutine + run_next_test(set, stats, &ctx); + + for (size_t i = 0; i < nb_workers; ++i) { + workers.workers[i] = run_next_test(NULL, NULL, &ctx); + if (!is_runner()) + goto cleanup; + + if (!ctx) + break; + ++active_workers; + } + + if (!active_workers) + goto cleanup; + + while ((ev = worker_read_event(&workers, event_pipe)) != NULL) { + handle_event(ev); + size_t wi = ev->worker_index; + if (ev->kind == WORKER_TERMINATED) { + sfree(workers.workers[wi]); + workers.workers[wi] = ctx ? run_next_test(NULL, NULL, &ctx) : NULL; + + if (!is_runner()) + goto cleanup; + + if (workers.workers[wi] == NULL) + --active_workers; + } + sfree(ev); + if (!active_workers) + break; + } + ev = NULL; + +cleanup: + sfree(event_pipe); + sfree(ev); + for (size_t i = 0; i < nb_workers; ++i) + sfree(workers.workers[i]); + free(workers.workers); + ccrAbort(ctx); } static int criterion_run_all_tests_impl(struct criterion_test_set *set) { report(PRE_ALL, set); log(pre_all, set); + if (RUNNING_ON_VALGRIND) { + if (!criterion_options.no_early_exit) + criterion_pimportant(CRITERION_PREFIX_DASHES, + _(msg_valgrind_early_exit), CR_FG_BOLD, CR_RESET); + if (criterion_options.jobs != 1) + criterion_pimportant(CRITERION_PREFIX_DASHES, + _(msg_valgrind_jobs), CR_FG_BOLD, CR_RESET); + } + fflush(NULL); // flush everything before forking g_worker_pipe = stdpipe(); - if (g_worker_pipe == NULL) + if (g_worker_pipe == NULL) { + criterion_perror("Could not initialize the event pipe: %s.\n", + strerror(errno)); abort(); + } + init_proc_compat(); struct criterion_global_stats *stats = stats_init(); - map_tests(set, stats, run_test_switch); + run_tests_async(set, stats); int result = is_runner() ? stats->tests_failed == 0 : -1; @@ -469,9 +462,11 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) { goto cleanup; report(POST_ALL, stats); + process_all_output(stats); log(post_all, stats); cleanup: + free_proc_compat(); sfree(g_worker_pipe); sfree(stats); return result; @@ -487,5 +482,31 @@ int criterion_run_all_tests(struct criterion_test_set *set) { int res = criterion_run_all_tests_impl(set); unset_runner_process(); + if (res == -1) { + criterion_finalize(set); + exit(0); + } + return criterion_options.always_succeed || res; } + +void run_single_test_by_name(const char *testname) { + struct criterion_test_set *set = criterion_init(); + + g_event_pipe = pipe_file_open(NULL); + + FOREACH_SET(struct criterion_suite_set *s, set->suites) { + size_t tests = s->tests ? s->tests->size : 0; + if (!tests) + continue; + + FOREACH_SET(struct criterion_test *t, s->tests) { + char name[1024]; + snprintf(name, sizeof (name), "%s::%s", s->suite.name, t->name); + if (!strncmp(name, testname, 1024)) + run_test_child(t, &s->suite); + } + } + + sfree(set); +} diff --git a/src/core/runner.h b/src/core/runner.h index 0b6ad050..49039bf2 100644 --- a/src/core/runner.h +++ b/src/core/runner.h @@ -27,10 +27,11 @@ # include "criterion/types.h" # include "compat/pipe.h" -DECL_SECTION_LIMITS(struct criterion_test*, cr_tst); -DECL_SECTION_LIMITS(struct criterion_suite*, cr_sts); +CR_DECL_SECTION_LIMITS(struct criterion_test*, cr_tst); +CR_DECL_SECTION_LIMITS(struct criterion_suite*, cr_sts); struct criterion_test_set *criterion_init(void); +void run_test_child(struct criterion_test *test, struct criterion_suite *suite); # define FOREACH_TEST_SEC(Test) \ for (struct criterion_test **Test = GET_SECTION_START(cr_tst); \ @@ -42,6 +43,6 @@ struct criterion_test_set *criterion_init(void); Suite < (struct criterion_suite**) GET_SECTION_END(cr_sts); \ ++Suite) -extern s_pipe_handle *g_worker_pipe; +void run_single_test_by_name(const char *testname); #endif /* !CRITERION_RUNNER_H_ */ diff --git a/src/core/runner_coroutine.c b/src/core/runner_coroutine.c new file mode 100644 index 00000000..57a091a5 --- /dev/null +++ b/src/core/runner_coroutine.c @@ -0,0 +1,178 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include "criterion/logging.h" +#include "criterion/internal/parameterized.h" +#include "runner_coroutine.h" +#include "worker.h" +#include "stats.h" +#include "runner.h" +#include "report.h" + +static INLINE void nothing(void) {} + +ccrBeginDefineContextType(run_next_context); + + struct criterion_suite_set *suite_set; + struct criterion_test *test; + struct criterion_suite_stats *suite_stats; + struct criterion_test_stats *test_stats; + struct criterion_test_set *set; + struct criterion_global_stats *stats; + struct criterion_test_params params; + + struct criterion_ordered_set_node *ns; + struct criterion_ordered_set_node *nt; + size_t i; + +ccrEndDefineContextType; + +static struct worker *run_test(struct criterion_global_stats *stats, + struct criterion_suite_stats *suite_stats, + struct criterion_test_stats *test_stats, + struct test_single_param *param) { + + struct execution_context ctx = { + .stats = sref(stats), + .test = test_stats->test, + .test_stats = sref(test_stats), + .suite = suite_stats->suite, + .suite_stats = sref(suite_stats), + .param = param, + }; + return spawn_test_worker(&ctx, run_test_child, g_worker_pipe); +} + +static INLINE bool is_disabled(struct criterion_test *t, + struct criterion_suite *s) { + + return t->data->disabled || (s->data && s->data->disabled); +} + + +static struct worker *cleanup_and_return_worker(struct run_next_context *ctx, + struct worker *worker) { + + sfree(ctx->test_stats); + if (!is_runner()) { + worker = NULL; + sfree(ctx->suite_stats); + if (ctx->test->data->kind_ == CR_TEST_PARAMETERIZED + && ctx->params.cleanup) + ctx->params.cleanup(&ctx->params); + } + return worker; +} + +static int skip_disabled(struct run_next_context *ctx) { + if (is_disabled(ctx->test, ctx->suite_stats->suite)) { + ctx->test_stats = test_stats_init(ctx->test); + stat_push_event(ctx->stats, + ctx->suite_stats, + ctx->test_stats, + &(struct event) { .kind = PRE_INIT }); + sfree(ctx->test_stats); + return 1; + } + return 0; +} + +struct worker *run_next_test(struct criterion_test_set *p_set, + struct criterion_global_stats *p_stats, + ccrContParam) { + struct worker *worker = NULL; + + ccrUseNamedContext(run_next_context, ctx); + + ccrBegin(ctx); + + ctx->set = p_set; + ctx->stats = p_stats; + ccrReturn(NULL); + + for (ctx->ns = ctx->set->suites->first; ctx->ns; ctx->ns = ctx->ns->next) { + ctx->suite_set = (void*) (ctx->ns + 1); + + if (!ctx->suite_set->tests) + continue; + + report(PRE_SUITE, ctx->suite_set); + log(pre_suite, ctx->suite_set); + + ctx->suite_stats = suite_stats_init(&ctx->suite_set->suite); + + stat_push_event(ctx->stats, ctx->suite_stats, NULL, &(struct event) { .kind = PRE_SUITE }); + + for (ctx->nt = ctx->suite_set->tests->first; ctx->nt; ctx->nt = ctx->nt->next) { + ctx->test = (void*) (ctx->nt + 1); + + if (ctx->test->data->kind_ == CR_TEST_PARAMETERIZED + && ctx->test->data->param_) { + + if (skip_disabled(ctx)) + continue; + + ctx->params = ctx->test->data->param_(); + for (ctx->i = 0; ctx->i < ctx->params.length; ++ctx->i) { + ctx->test_stats = test_stats_init(ctx->test); + + + worker = run_test(ctx->stats, + ctx->suite_stats, + ctx->test_stats, + &(struct test_single_param) { + ctx->params.size, + (char *) ctx->params.params + ctx->i * ctx->params.size + }); + + ccrReturn(cleanup_and_return_worker(ctx, worker)); + } + + if (ctx->params.cleanup) + ctx->params.cleanup(&ctx->params); + } else { + ctx->test_stats = test_stats_init(ctx->test); + + if (skip_disabled(ctx)) + continue; + + worker = run_test(ctx->stats, + ctx->suite_stats, + ctx->test_stats, + NULL); + + ccrReturn(cleanup_and_return_worker(ctx, worker)); + } + } + + report(POST_SUITE, ctx->suite_stats); + log(post_suite, ctx->suite_stats); + + sfree(ctx->suite_stats); + } + + ccrFinish(NULL); +} diff --git a/src/core/runner_coroutine.h b/src/core/runner_coroutine.h new file mode 100644 index 00000000..0b8773a3 --- /dev/null +++ b/src/core/runner_coroutine.h @@ -0,0 +1,33 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef RUNNER_COROUTINE_H_ +# define RUNNER_COROUTINE_H_ + +# include "coroutine.h" + +struct worker *run_next_test(struct criterion_test_set *p_set, + struct criterion_global_stats *p_stats, + ccrContParam); + +#endif /* !RUNNER_COROUTINE_H_ */ diff --git a/src/core/stats.c b/src/core/stats.c index 5d020e12..f8d46cdb 100644 --- a/src/core/stats.c +++ b/src/core/stats.c @@ -23,7 +23,7 @@ */ #include #include -#include "criterion/common.h" +#include "criterion/internal/common.h" #include "stats.h" #include "common.h" @@ -55,13 +55,13 @@ static void push_test_crash(s_glob_stats *stats, s_test_stats *tstats, void *data); -static void nothing(UNUSED s_glob_stats *stats, - UNUSED s_suite_stats *sstats, - UNUSED s_test_stats *tstats, - UNUSED void *data) { -}; +static void nothing(CR_UNUSED s_glob_stats *stats, + CR_UNUSED s_suite_stats *sstats, + CR_UNUSED s_test_stats *tstats, + CR_UNUSED void *data) { +} -static void destroy_stats(void *ptr, UNUSED void *meta) { +static void destroy_stats(void *ptr, CR_UNUSED void *meta) { s_glob_stats *stats = ptr; for (s_suite_stats *s = stats->suites, *next; s; s = next) { next = s->next; @@ -72,13 +72,14 @@ static void destroy_stats(void *ptr, UNUSED void *meta) { s_glob_stats *stats_init(void) { s_glob_stats *stats = smalloc( .size = sizeof (s_glob_stats), + .kind = SHARED, .dtor = destroy_stats ); *stats = (s_glob_stats) { .suites = NULL }; return stats; } -static void destroy_suite_stats(void *ptr, UNUSED void *meta) { +static void destroy_suite_stats(void *ptr, CR_UNUSED void *meta) { s_suite_stats *stats = ptr; for (s_test_stats *t = stats->tests, *next; t; t = next) { next = t->next; @@ -96,7 +97,7 @@ s_suite_stats *suite_stats_init(struct criterion_suite *s) { return stats; } -static void destroy_test_stats(void *ptr, UNUSED void *meta) { +static void destroy_test_stats(void *ptr, CR_UNUSED void *meta) { s_test_stats *stats = ptr; for (s_assert_stats *a = stats->asserts, *next; a; a = next) { next = a->next; @@ -104,6 +105,11 @@ static void destroy_test_stats(void *ptr, UNUSED void *meta) { } } +static void destroy_assert_stats(void *ptr, CR_UNUSED void *meta) { + s_assert_stats *stats = ptr; + free((void *) stats->message); +} + s_test_stats *test_stats_init(struct criterion_test *t) { s_test_stats *stats = smalloc( .size = sizeof (s_test_stats), @@ -146,8 +152,8 @@ void stat_push_event(s_glob_stats *stats, static void push_pre_suite(s_glob_stats *stats, s_suite_stats *suite, - UNUSED s_test_stats *test, - UNUSED void *ptr) { + CR_UNUSED s_test_stats *test, + CR_UNUSED void *ptr) { suite->next = stats->suites; stats->suites = sref(suite); ++stats->nb_suites; @@ -162,7 +168,7 @@ static INLINE bool is_disabled(struct criterion_test *t, static void push_pre_init(s_glob_stats *stats, s_suite_stats *suite, s_test_stats *test, - UNUSED void *ptr) { + CR_UNUSED void *ptr) { test->next = suite->tests; suite->tests = sref(test); ++stats->nb_tests; @@ -181,7 +187,9 @@ static void push_assert(s_glob_stats *stats, s_assert_stats *data = ptr; - s_assert_stats *dup = smalloc(sizeof (s_assert_stats)); + s_assert_stats *dup = smalloc( + .size = sizeof (s_assert_stats), + .dtor = destroy_assert_stats); memcpy(dup, data, sizeof (s_assert_stats)); dup->message = strdup(data->message); @@ -229,8 +237,9 @@ static void push_post_test(s_glob_stats *stats, static void push_test_crash(s_glob_stats *stats, s_suite_stats *suite, s_test_stats *test, - UNUSED void *ptr) { + CR_UNUSED void *ptr) { test->failed = 1; + test->crashed = 1; ++suite->tests_failed; ++suite->tests_crashed; ++stats->tests_failed; diff --git a/src/core/test.c b/src/core/test.c new file mode 100644 index 00000000..4295360d --- /dev/null +++ b/src/core/test.c @@ -0,0 +1,79 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "criterion/internal/test.h" +#include "core/abort.h" +#include "core/stats.h" +#include "core/worker.h" +#include "core/report.h" +#include "compat/time.h" +#include "io/event.h" + +extern const struct criterion_test *criterion_current_test; +extern const struct criterion_suite *criterion_current_suite; + +static INLINE void nothing(void) {} + +void criterion_internal_test_setup(void) { + const struct criterion_suite *suite = criterion_current_suite; + const struct criterion_test *test = criterion_current_test; + + criterion_send_event(PRE_INIT, NULL, 0); + if (suite->data) + (suite->data->init ? suite->data->init : nothing)(); + (test->data->init ? test->data->init : nothing)(); +} + +void criterion_internal_test_main(void (*fn)(void)) { + const struct criterion_test *test = criterion_current_test; + + criterion_send_event(PRE_TEST, NULL, 0); + + struct timespec_compat ts; + if (!setjmp(g_pre_test)) { + timer_start(&ts); + if (!test->data->param_) { + fn(); + } else { + void(*param_test_func)(void *) = (void(*)(void*)) fn; + param_test_func(g_worker_context.param->ptr); + } + } + + double elapsed_time; + if (!timer_end(&elapsed_time, &ts)) + elapsed_time = -1; + + criterion_send_event(POST_TEST, &elapsed_time, sizeof(double)); +} + +void criterion_internal_test_teardown(void) { + const struct criterion_suite *suite = criterion_current_suite; + const struct criterion_test *test = criterion_current_test; + + (test->data->fini ? test->data->fini : nothing)(); + if (suite->data) + (suite->data->fini ? suite->data->fini : nothing)(); + criterion_send_event(POST_FINI, NULL, 0); +} + diff --git a/src/core/theories.c b/src/core/theories.c index 1af29bcb..28d0d127 100644 --- a/src/core/theories.c +++ b/src/core/theories.c @@ -222,7 +222,7 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (* concat_arg(&result.msg, dps, indices, datapoints - 1); result.len = strlen(result.msg) + 1; - send_event(THEORY_FAIL, &result, result.len + sizeof (size_t)); + criterion_send_event(THEORY_FAIL, &result, result.len + sizeof(size_t)); } } diff --git a/src/core/worker.c b/src/core/worker.c index 85a945a5..54d68684 100644 --- a/src/core/worker.c +++ b/src/core/worker.c @@ -23,6 +23,7 @@ */ #include #include +#include #include #include "criterion/types.h" @@ -32,11 +33,6 @@ #include "compat/posix.h" #include "worker.h" -struct process { - s_proc_handle *proc; - FILE *in; -}; - static s_proc_handle *g_current_proc; void set_runner_process(void) { @@ -51,22 +47,42 @@ bool is_runner(void) { return is_current_process(g_current_proc); } -static void close_process(void *ptr, UNUSED void *meta) { - struct process *proc = ptr; - fclose(proc->in); +static void close_process(void *ptr, CR_UNUSED void *meta) { + struct worker *proc = ptr; + sfree(proc->in); + sfree(proc->ctx.suite_stats); + sfree(proc->ctx.test_stats); + sfree(proc->ctx.stats); sfree(proc->proc); } -struct event *worker_read_event(struct process *proc) { - return read_event(proc->in); +struct event *worker_read_event(struct worker_set *workers, s_pipe_file_handle *pipe) { + struct event *ev = read_event(pipe); + if (ev) { + ev->worker_index = -1; + for (size_t i = 0; i < workers->max_workers; ++i) { + if (!workers->workers[i]) + continue; + + if (get_process_id_of(workers->workers[i]->proc) == ev->pid) { + ev->worker = workers->workers[i]; + ev->worker_index = i; + return ev; + } + } + criterion_perror("Could not link back the event PID to the active workers.\n"); + criterion_perror("The event pipe might have been corrupted.\n"); + abort(); + } + return NULL; } void run_worker(struct worker_context *ctx) { cr_redirect_stdin(); - g_event_pipe = pipe_out(ctx->pipe, PIPE_CLOSE); + g_event_pipe = pipe_out_handle(ctx->pipe, PIPE_CLOSE); ctx->func(ctx->test, ctx->suite); - fclose(g_event_pipe); + sfree(g_event_pipe); fflush(NULL); // flush all opened streams if (criterion_options.no_early_exit) @@ -74,35 +90,41 @@ void run_worker(struct worker_context *ctx) { _Exit(0); } -struct process *spawn_test_worker(struct criterion_test *test, - struct criterion_suite *suite, - f_worker_func func, - s_pipe_handle *pipe, - struct test_single_param *param) { +struct worker *spawn_test_worker(struct execution_context *ctx, + cr_worker_func func, + s_pipe_handle *pipe) { g_worker_context = (struct worker_context) { - .test = test, - .suite = suite, + .test = ctx->test, + .suite = ctx->suite, .func = func, .pipe = pipe, - .param = param, + .param = ctx->param, }; - struct process *ptr = NULL; + struct worker *ptr = NULL; s_proc_handle *proc = fork_process(); if (proc == (void *) -1) { + criterion_perror("Could not fork the current process and start a worker: %s.\n", strerror(errno)); abort(); } else if (proc == NULL) { run_worker(&g_worker_context); + sfree(ctx->test_stats); + sfree(ctx->suite_stats); + sfree(ctx->stats); return NULL; } ptr = smalloc( - .size = sizeof (struct process), - .kind = UNIQUE, + .size = sizeof (struct worker), + .kind = SHARED, .dtor = close_process); - *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, PIPE_DUP) }; + *ptr = (struct worker) { + .proc = proc, + .in = pipe_in_handle(pipe, PIPE_DUP), + .ctx = *ctx, + }; return ptr; } @@ -121,10 +143,3 @@ struct process_status get_status(int status) { return (struct process_status) { .kind = STOPPED }; } - -struct process_status wait_proc(struct process *proc) { - int status; - wait_process(proc->proc, &status); - - return get_status(status); -} diff --git a/src/core/worker.h b/src/core/worker.h index ab8bff91..414f374c 100644 --- a/src/core/worker.h +++ b/src/core/worker.h @@ -29,7 +29,30 @@ # include "compat/process.h" # include "compat/pipe.h" -struct process; +struct test_single_param { + size_t size; + void *ptr; +}; + +struct execution_context { + bool test_started; + bool normal_finish; + bool cleaned_up; + bool aborted; + struct criterion_global_stats *stats; + struct criterion_test *test; + struct criterion_test_stats *test_stats; + struct criterion_suite *suite; + struct criterion_suite_stats *suite_stats; + struct test_single_param *param; +}; + +struct worker { + int active; + s_proc_handle *proc; + s_pipe_file_handle *in; + struct execution_context ctx; +}; enum status_kind { EXIT_STATUS, @@ -47,22 +70,22 @@ struct worker_status { struct process_status status; }; -struct test_single_param { - size_t size; - void *ptr; +struct worker_set { + struct worker **workers; + size_t max_workers; }; +extern s_pipe_handle *g_worker_pipe; + void run_worker(struct worker_context *ctx); void set_runner_process(void); void unset_runner_process(void); bool is_runner(void); -struct process_status wait_proc(struct process *proc); +struct process_status wait_proc(struct worker *proc); struct process_status get_status(int status); -struct process *spawn_test_worker(struct criterion_test *test, - struct criterion_suite *suite, - f_worker_func func, - s_pipe_handle *pipe, - struct test_single_param *param); -struct event *worker_read_event(struct process *proc); +struct worker *spawn_test_worker(struct execution_context *ctx, + cr_worker_func func, + s_pipe_handle *pipe); +struct event *worker_read_event(struct worker_set *workers, s_pipe_file_handle *pipe); #endif /* !PROCESS_H_ */ diff --git a/src/entry/entry.c b/src/entry/entry.c index 71e9b9d0..2e57189f 100644 --- a/src/entry/entry.c +++ b/src/entry/entry.c @@ -1,12 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #include "criterion/criterion.h" CR_API int main(int argc, char *argv[]) { struct criterion_test_set *tests = criterion_initialize(); - if (!criterion_handle_args(argc, argv, true)) - return 0; - - int result = !criterion_run_all_tests(tests); + int result = 0; + if (criterion_handle_args(argc, argv, true)) + result = !criterion_run_all_tests(tests); criterion_finalize(tests); diff --git a/src/entry/options.c b/src/entry/options.c index c83b294a..0569a402 100644 --- a/src/entry/options.c +++ b/src/entry/options.c @@ -25,5 +25,6 @@ struct criterion_options criterion_options = { .logging_threshold = CRITERION_IMPORTANT, - .output_provider = &normal_logging, + .logger = &normal_logging, + .measure_time = true, }; diff --git a/src/entry/main.c b/src/entry/params.c similarity index 55% rename from src/entry/main.c rename to src/entry/params.c index 5551886b..a6dc8a09 100644 --- a/src/entry/main.c +++ b/src/entry/params.c @@ -28,8 +28,11 @@ #include #include "criterion/criterion.h" #include "criterion/options.h" -#include "criterion/ordered-set.h" +#include "criterion/internal/ordered-set.h" +#include "compat/posix.h" +#include "compat/strtok.h" #include "core/runner.h" +#include "io/output.h" #include "config.h" #include "common.h" @@ -52,21 +55,28 @@ "usage: %s OPTIONS\n" \ "options: \n" \ " -h or --help: prints this message\n" \ + " -q or --quiet: disables all logging\n" \ " -v or --version: prints the version of criterion " \ "these tests have been linked against\n" \ " -l or --list: prints all the tests in a list\n" \ + " -jN or --jobs N: use N concurrent jobs\n" \ " -f or --fail-fast: exit after the first failure\n" \ " --ascii: don't use fancy unicode symbols " \ "or colors in the output\n" \ " -S or --short-filename: only display the base " \ "name of the source file on a failure\n" \ PATTERN_USAGE \ - " --tap: enables TAP formatting\n" \ + " --tap[=FILE]: writes TAP report in FILE " \ + "(no file or \"-\" means stderr)\n" \ + " --xml[=FILE]: writes XML report in FILE " \ + "(no file or \"-\" means stderr)\n" \ " --always-succeed: always exit with 0\n" \ " --no-early-exit: do not exit the test worker " \ "prematurely after the test\n" \ " --verbose[=level]: sets verbosity to level " \ - "(1 by default)\n" + "(1 by default)\n" \ + " -OP:F or --output=PROVIDER=FILE: write test " \ + "report to FILE using the specified provider\n" int print_usage(char *progname) { fprintf(stderr, USAGE, progname); @@ -102,7 +112,7 @@ int list_tests(bool unicode) { if (!tests) continue; - printf("%s: " SIZE_T_FORMAT " test%s\n", + printf("%s: " CR_SIZE_T_FORMAT " test%s\n", s->suite.name, tests, tests == 1 ? "" : "s"); @@ -120,68 +130,166 @@ int list_tests(bool unicode) { return 0; } +int atou(const char *str) { + int res = atoi(str); + return res < 0 ? 0 : res; +} + int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg) { static struct option opts[] = { {"verbose", optional_argument, 0, 'b'}, + {"quiet", no_argument, 0, 'q'}, {"version", no_argument, 0, 'v'}, - {"tap", no_argument, 0, 't'}, + {"tap", optional_argument, 0, 't'}, + {"xml", optional_argument, 0, 'x'}, + {"json", optional_argument, 0, 'n'}, {"help", no_argument, 0, 'h'}, {"list", no_argument, 0, 'l'}, {"ascii", no_argument, 0, 'k'}, + {"jobs", required_argument, 0, 'j'}, {"fail-fast", no_argument, 0, 'f'}, {"short-filename", no_argument, 0, 'S'}, + {"single", required_argument, 0, 's'}, #ifdef HAVE_PCRE {"pattern", required_argument, 0, 'p'}, #endif {"always-succeed", no_argument, 0, 'y'}, {"no-early-exit", no_argument, 0, 'z'}, + {"output", required_argument, 0, 'O'}, {0, 0, 0, 0 } }; - bool use_ascii = !strcmp("1", DEF(getenv("CRITERION_USE_ASCII"), "0")) - || !strcmp("dumb", DEF(getenv("TERM"), "dumb")); - setlocale(LC_ALL, ""); #if ENABLE_NLS textdomain (PACKAGE "-test"); #endif + if (!handle_unknown_arg) + opterr = 0; + + char *env_always_succeed = getenv("CRITERION_ALWAYS_SUCCEED"); + char *env_no_early_exit = getenv("CRITERION_NO_EARLY_EXIT"); + char *env_fail_fast = getenv("CRITERION_FAIL_FAST"); + char *env_use_ascii = getenv("CRITERION_USE_ASCII"); + char *env_jobs = getenv("CRITERION_JOBS"); + char *env_logging_threshold = getenv("CRITERION_VERBOSITY_LEVEL"); + char *env_short_filename = getenv("CRITERION_SHORT_FILENAME"); + + bool is_term_dumb = !strcmp("dumb", DEF(getenv("TERM"), "dumb")); + struct criterion_options *opt = &criterion_options; - opt->always_succeed = !strcmp("1", DEF(getenv("CRITERION_ALWAYS_SUCCEED"), "0")); - opt->no_early_exit = !strcmp("1", DEF(getenv("CRITERION_NO_EARLY_EXIT") , "0")); - opt->fail_fast = !strcmp("1", DEF(getenv("CRITERION_FAIL_FAST") , "0")); - opt->use_ascii = use_ascii; - opt->logging_threshold = atoi(DEF(getenv("CRITERION_VERBOSITY_LEVEL"), "2")); - opt->short_filename = !strcmp("1", DEF(getenv("CRITERION_SHORT_FILENAME"), "0")); + if (env_always_succeed) + opt->always_succeed = !strcmp("1", env_always_succeed); + if (env_no_early_exit) + opt->no_early_exit = !strcmp("1", env_no_early_exit); + if (env_fail_fast) + opt->fail_fast = !strcmp("1", env_fail_fast); + if (env_use_ascii) + opt->use_ascii = !strcmp("1", env_use_ascii) || is_term_dumb; + if (env_jobs) + opt->jobs = atou(env_jobs); + if (env_logging_threshold) + opt->logging_threshold = (enum criterion_logging_level) atou(env_logging_threshold); + if (env_short_filename) + opt->short_filename = !strcmp("1", env_short_filename); + #ifdef HAVE_PCRE - opt->pattern = getenv("CRITERION_TEST_PATTERN"); + char *env_pattern = getenv("CRITERION_TEST_PATTERN"); + if (env_pattern) + opt->pattern = env_pattern; #endif - bool use_tap = !strcmp("1", DEF(getenv("CRITERION_ENABLE_TAP"), "0")); + opt->measure_time = !!strcmp("1", DEF(getenv("CRITERION_DISABLE_TIME_MEASUREMENTS"), "0")); + + bool quiet = false; + + // CRITERION_ENABLE_TAP backward compatibility. + // The environment variable is otherwise deprecated. + if (!strcmp("1", DEF(getenv("CRITERION_ENABLE_TAP"), "0"))) { + quiet = true; + criterion_add_output("tap", DEF(optarg, "-")); + } bool do_list_tests = false; bool do_print_version = false; bool do_print_usage = false; - for (int c; (c = getopt_long(argc, argv, "hvlfS", opts, NULL)) != -1;) { + + const char *outputs = getenv("CRITERION_OUTPUTS"); + if (outputs) { + char *out = strdup(outputs); + char *buf = NULL; + strtok_r(out, ",", &buf); + + for (char *s = out; s; s = strtok_r(NULL, ",", &buf)) { + s = strdup(s); + char *buf2 = NULL; + char *provider = strtok_r(s, ":", &buf2); + char *path = strtok_r(NULL, ":", &buf2); + + if (provider == NULL || path == NULL) { + do_print_usage = true; + goto end; + } + + quiet = true; + criterion_add_output(provider, path); + } + free(out); + } + + for (int c; (c = getopt_long(argc, argv, "hvlfj:SqO:", opts, NULL)) != -1;) { switch (c) { - case 'b': criterion_options.logging_threshold = atoi(DEF(optarg, "1")); break; + case 'b': criterion_options.logging_threshold = (enum criterion_logging_level) atou(DEF(optarg, "1")); break; case 'y': criterion_options.always_succeed = true; break; case 'z': criterion_options.no_early_exit = true; break; case 'k': criterion_options.use_ascii = true; break; + case 'j': criterion_options.jobs = atou(optarg); break; case 'f': criterion_options.fail_fast = true; break; case 'S': criterion_options.short_filename = true; break; + case 's': run_single_test_by_name(optarg); return 0; #ifdef HAVE_PCRE case 'p': criterion_options.pattern = optarg; break; #endif - case 't': use_tap = true; break; + case 'q': quiet = true; break; + + { + const char *provider; + case 't': provider = "tap"; goto provider_def; + case 'x': provider = "xml"; goto provider_def; + case 'n': provider = "json"; goto provider_def; + + provider_def: {} + const char *path = DEF(optarg, "-"); + quiet = !strcmp(path, "-"); + criterion_add_output(provider, path); + } break; + case 'l': do_list_tests = true; break; case 'v': do_print_version = true; break; case 'h': do_print_usage = true; break; + case 'O': { + char *arg = strdup(optarg); + char *buf = NULL; + strtok_r(arg, ":", &buf); + + char *path = strtok_r(NULL, ":", &buf); + if (arg == NULL || path == NULL) { + do_print_usage = true; + break; + } + + quiet = !strcmp(path, "-"); + criterion_add_output(arg, path); + } break; + case '?': default : do_print_usage = handle_unknown_arg; break; } } - if (use_tap) - criterion_options.output_provider = TAP_LOGGING; + +end: + if (quiet) + criterion_options.logging_threshold = CRITERION_LOG_LEVEL_QUIET; + if (do_print_usage) return print_usage(argv[0]); if (do_print_version) diff --git a/src/io/asprintf.c b/src/io/asprintf.c index c2a4a126..f340b1d0 100644 --- a/src/io/asprintf.c +++ b/src/io/asprintf.c @@ -25,7 +25,7 @@ #include #include #include -#include "criterion/asprintf-compat.h" +#include "criterion/internal/asprintf-compat.h" int cr_asprintf(char **strp, const char *fmt, ...) { va_list ap; diff --git a/src/io/event.c b/src/io/event.c index 1ba6a26f..8d45c4b9 100644 --- a/src/io/event.c +++ b/src/io/event.c @@ -26,28 +26,45 @@ #include #include #include "criterion/stats.h" -#include "criterion/common.h" +#include "criterion/internal/common.h" #include "criterion/hooks.h" +#include "criterion/logging.h" #include "core/worker.h" #include "event.h" -FILE *g_event_pipe = NULL; +s_pipe_file_handle *g_event_pipe = NULL; -void destroy_event(void *ptr, UNUSED void *meta) { +void destroy_event(void *ptr, CR_UNUSED void *meta) { struct event *ev = ptr; free(ev->data); } -void destroy_assert_event(void *ptr, UNUSED void *meta) { +void destroy_assert_event(void *ptr, CR_UNUSED void *meta) { struct event *ev = ptr; free((void*) ((struct criterion_assert_stats *) ev->data)->message); free(ev->data); } -struct event *read_event(FILE *f) { +#ifdef __GNUC__ +# define unlikely(x) __builtin_expect((x),0) +#else +# define unlikely(x) (x) +#endif + +#define ASSERT(Cond) \ + do { \ + if (unlikely(!(Cond))){ \ + criterion_perror("Corrupted event IO in the worker pipe.\n"); \ + abort(); \ + } \ + } while (0) + +struct event *read_event(s_pipe_file_handle *f) { unsigned kind; - if (fread(&kind, sizeof (unsigned), 1, f) == 0) - return NULL; + ASSERT(pipe_read(&kind, sizeof (unsigned), f) == 1); + + unsigned long long pid; + ASSERT(pipe_read(&pid, sizeof (unsigned long long), f) == 1); switch (kind) { case ASSERT: { @@ -56,16 +73,13 @@ struct event *read_event(FILE *f) { char *msg = NULL; buf = malloc(assert_size); - if (fread(buf, assert_size, 1, f) == 0) - goto fail_assert; + ASSERT(pipe_read(buf, assert_size, f) == 1); size_t len = 0; - if (fread(&len, sizeof (size_t), 1, f) == 0) - goto fail_assert; + ASSERT(pipe_read(&len, sizeof (size_t), f) == 1); msg = malloc(len); - if (fread(msg, len, 1, f) == 0) - goto fail_assert; + ASSERT(pipe_read(msg, len, f) == 1); buf->message = msg; @@ -73,73 +87,77 @@ struct event *read_event(FILE *f) { .size = sizeof (struct event), .dtor = destroy_assert_event ); - *ev = (struct event) { .kind = kind, .data = buf }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = buf }; return ev; + } + case TEST_ABORT: { + char *msg = NULL; + + size_t len = 0; + ASSERT(pipe_read(&len, sizeof (size_t), f) == 1); + + msg = malloc(len); + ASSERT(pipe_read(msg, len, f) == 1); -fail_assert: - free(buf); - free(msg); - return NULL; + struct event *ev = smalloc( + .size = sizeof (struct event), + .dtor = destroy_event + ); + *ev = (struct event) { .pid = pid, .kind = kind, .data = msg }; + return ev; } case THEORY_FAIL: { size_t len = 0; - if (fread(&len, sizeof (size_t), 1, f) == 0) - return NULL; + ASSERT(pipe_read(&len, sizeof (size_t), f) == 1); char *buf = malloc(len); - if (fread(buf, len, 1, f) == 0) { - free(buf); - return NULL; - } + ASSERT(pipe_read(buf, len, f) == 1); struct event *ev = smalloc( .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = buf }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = buf }; return ev; } case POST_TEST: { double *elapsed_time = malloc(sizeof (double)); - if (fread(elapsed_time, sizeof (double), 1, f) == 0) { - free(elapsed_time); - return NULL; - } + ASSERT(pipe_read(elapsed_time, sizeof (double), f) == 1); struct event *ev = smalloc( .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = elapsed_time }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = elapsed_time }; return ev; } case WORKER_TERMINATED: { struct worker_status *status = malloc(sizeof (struct worker_status)); - if (fread(status, sizeof (struct worker_status), 1, f) == 0) { - free(status); - return NULL; - } + ASSERT(pipe_read(status, sizeof (struct worker_status), f) == 1); struct event *ev = smalloc( .size = sizeof (struct event), .dtor = destroy_event ); - *ev = (struct event) { .kind = kind, .data = status }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = status }; return ev; } default: { struct event *ev = smalloc(sizeof (struct event)); - *ev = (struct event) { .kind = kind, .data = NULL }; + *ev = (struct event) { .pid = pid, .kind = kind, .data = NULL }; return ev; } } } -void send_event(int kind, void *data, size_t size) { - unsigned char *buf = malloc(sizeof (int) + size); +void criterion_send_event(int kind, void *data, size_t size) { + unsigned long long pid = get_process_id(); + + unsigned char *buf = malloc(sizeof (int) + sizeof (pid) + size); memcpy(buf, &kind, sizeof (int)); - memcpy(buf + sizeof (int), data, size); - if (fwrite(buf, sizeof (int) + size, 1, g_event_pipe) == 0) - abort(); + memcpy(buf + sizeof (int), &pid, sizeof (pid)); + memcpy(buf + sizeof (int) + sizeof (pid), data, size); + ASSERT(pipe_write(buf, sizeof (int) + sizeof (pid) + size, g_event_pipe) == 1); + free(buf); } diff --git a/src/io/event.h b/src/io/event.h index 64c30acd..e4e555ba 100644 --- a/src/io/event.h +++ b/src/io/event.h @@ -25,19 +25,25 @@ # define EVENT_H_ # include "criterion/event.h" +# include "core/worker.h" # include -extern FILE *g_event_pipe; +extern s_pipe_file_handle *g_event_pipe; struct event { + unsigned long long pid; int kind; void *data; + + struct worker *worker; + size_t worker_index; }; enum other_event_kinds { WORKER_TERMINATED = 1 << 30, + TEST_ABORT, }; -struct event *read_event(FILE *f); +struct event *read_event(s_pipe_file_handle *f); #endif /* !EVENT_H_ */ diff --git a/src/io/json.c b/src/io/json.c new file mode 100644 index 00000000..ea600731 --- /dev/null +++ b/src/io/json.c @@ -0,0 +1,206 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define _GNU_SOURCE +#include +#include +#include +#include "criterion/stats.h" +#include "criterion/logging.h" +#include "criterion/options.h" +#include "criterion/internal/ordered-set.h" +#include "compat/posix.h" +#include "compat/strtok.h" +#include "compat/time.h" +#include "config.h" +#include "common.h" + +#define JSON_TEST_TEMPLATE_BEGIN \ + " {\n" \ + " \"name\": \"%s\",\n" \ + " \"assertions\": " CR_SIZE_T_FORMAT ",\n" \ + " \"status\": \"%s\"" + +#define JSON_TEST_TEMPLATE_END \ + "\n" \ + " }" + +#define JSON_TEST_FAILED_TEMPLATE_BEGIN \ + ",\n" \ + " \"messages\": [\n" + +#define JSON_TEST_FAILED_TEMPLATE_END \ + "\n" \ + " ]" + +#define JSON_FAILURE_MSG_ENTRY \ + " \"%s:%u: %s\"" + +#define JSON_CRASH_MSG_ENTRY \ + ",\n" \ + " \"messages\": [\"The test crashed.\"]" + +#define JSON_TIMEOUT_MSG_ENTRY \ + ",\n" \ + " \"messages\": [\"The test timed out.\"]" + +#define JSON_SKIPPED_MSG_ENTRY \ + ",\n" \ + " \"messages\": [\"The test was skipped.\"]" + +#define JSON_TEST_LIST_TEMPLATE_BEGIN \ + " \"tests\": [\n" + +#define JSON_TEST_LIST_TEMPLATE_END \ + " ]\n" + +#define JSON_TESTSUITE_TEMPLATE_BEGIN \ + " {\n" \ + " \"name\": \"%s\",\n" \ + " \"passed\": " CR_SIZE_T_FORMAT ",\n" \ + " \"failed\": " CR_SIZE_T_FORMAT ",\n" \ + " \"errored\": " CR_SIZE_T_FORMAT ",\n" \ + " \"skipped\": " CR_SIZE_T_FORMAT ",\n" + +#define JSON_TESTSUITE_TEMPLATE_END \ + " }" + +#define JSON_TESTSUITE_LIST_TEMPLATE_BEGIN \ + " \"test_suites\": [\n" + +#define JSON_TESTSUITE_LIST_TEMPLATE_END \ + " ]\n" + +#define JSON_BASE_TEMPLATE_BEGIN \ + "{\n" \ + " \"id\": \"Criterion v" VERSION "\",\n" \ + " \"passed\": " CR_SIZE_T_FORMAT ",\n" \ + " \"failed\": " CR_SIZE_T_FORMAT ",\n" \ + " \"errored\": " CR_SIZE_T_FORMAT ",\n" \ + " \"skipped\": " CR_SIZE_T_FORMAT ",\n" \ + +#define JSON_BASE_TEMPLATE_END \ + "}\n" + +static INLINE bool is_disabled(struct criterion_test *t, struct criterion_suite *s) { + return t->data->disabled || (s->data && s->data->disabled); +} + +static CR_INLINE +const char *get_status_string(struct criterion_test_stats *ts, + struct criterion_suite_stats *ss) { + + const char *status = "PASSED"; + if (ts->crashed || ts->timed_out) + status = "ERRORED"; + else if (ts->failed) + status = "FAILED"; + else if (is_disabled(ts->test, ss->suite)) + status = "SKIPPED"; + return status; +} + +static void print_test(FILE *f, + struct criterion_test_stats *ts, + struct criterion_suite_stats *ss) { + + fprintf(f, JSON_TEST_TEMPLATE_BEGIN, + ts->test->name, + (size_t) (ts->passed_asserts + ts->failed_asserts), + get_status_string(ts, ss) + ); + + if (is_disabled(ts->test, ss->suite)) { + fprintf(f, JSON_SKIPPED_MSG_ENTRY); + } else if (ts->crashed) { + fprintf(f, JSON_CRASH_MSG_ENTRY); + } else if (ts->timed_out) { + fprintf(f, JSON_TIMEOUT_MSG_ENTRY); + } else if (ts->failed) { + fprintf(f, JSON_TEST_FAILED_TEMPLATE_BEGIN); + + bool first = true; + for (struct criterion_assert_stats *asrt = ts->asserts; asrt; asrt = asrt->next) { + if (!asrt->passed) { + if (!first) { + fprintf(f, ",\n"); + } else { + first = false; + } + + bool sf = criterion_options.short_filename; + char *dup = strdup(*asrt->message ? asrt->message : ""); + char *saveptr = NULL; + char *line = strtok_r(dup, "\n", &saveptr); + + fprintf(f, JSON_FAILURE_MSG_ENTRY, + sf ? basename_compat(asrt->file) : asrt->file, + asrt->line, + line + ); + + while ((line = strtok_r(NULL, "\n", &saveptr))) { + fprintf(f, ",\n \" %s\"", line); + } + free(dup); + } + } + fprintf(f, JSON_TEST_FAILED_TEMPLATE_END); + } + + fprintf(f, JSON_TEST_TEMPLATE_END); +} + +void json_report(FILE *f, struct criterion_global_stats *stats) { + fprintf(f, JSON_BASE_TEMPLATE_BEGIN, + stats->tests_passed, + stats->tests_failed, + stats->tests_crashed, + stats->tests_skipped + ); + + fprintf(f, JSON_TESTSUITE_LIST_TEMPLATE_BEGIN); + for (struct criterion_suite_stats *ss = stats->suites; ss; ss = ss->next) { + + fprintf(f, JSON_TESTSUITE_TEMPLATE_BEGIN, + ss->suite->name, + ss->tests_passed, + ss->tests_failed, + ss->tests_crashed, + ss->tests_skipped + ); + + fprintf(f, JSON_TEST_LIST_TEMPLATE_BEGIN); + for (struct criterion_test_stats *ts = ss->tests; ts; ts = ts->next) { + print_test(f, ts, ss); + fprintf(f, ts->next ? ",\n" : "\n"); + } + fprintf(f, JSON_TEST_LIST_TEMPLATE_END); + + fprintf(f, JSON_TESTSUITE_TEMPLATE_END); + fprintf(f, ss->next ? ",\n" : "\n"); + } + fprintf(f, JSON_TESTSUITE_LIST_TEMPLATE_END); + + fprintf(f, JSON_BASE_TEMPLATE_END); +} diff --git a/src/io/output.c b/src/io/output.c new file mode 100644 index 00000000..d3fd05d1 --- /dev/null +++ b/src/io/output.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include "criterion/output.h" +#include "criterion/logging.h" +#include "string/i18n.h" + +typedef const char *const msg_t; + +#ifdef ENABLE_NLS +static msg_t msg_err = N_("Could not open the file @ `%1$s` for %2$s reporting: %3$s.\n"); +static msg_t msg_ok = N_("Writing %1$s report in `%2$s`.\n"); +#else +static msg_t msg_err = "Could not open the file @ `%s` for %s reporting: %s.\n"; +static msg_t msg_ok = "Writing %s report in `%s`.\n"; +#endif + +typedef kvec_t(const char *) str_vec; + +KHASH_MAP_INIT_STR(ht_str, criterion_reporter*) +KHASH_MAP_INIT_STR(ht_path, str_vec*) + +static khash_t(ht_str) *reporters; +static khash_t(ht_path) *outputs; + +int criterion_register_output_provider(const char *name, criterion_reporter *reporter) { + if (!reporters) + reporters = kh_init(ht_str); + + int absent; + khint_t k = kh_put(ht_str, reporters, name, &absent); + kh_value(reporters, k) = reporter; + return absent; +} + +int criterion_add_output(const char *provider, const char *path) { + if (!outputs) + outputs = kh_init(ht_path); + + khint_t k = kh_get(ht_path, outputs, provider); + + if (k == kh_end(outputs)) { + int absent; + k = kh_put(ht_path, outputs, provider, &absent); + if (absent == -1) + return -1; + + str_vec *vec = malloc(sizeof (str_vec)); + kv_init(*vec); + kh_value(outputs, k) = vec; + } + str_vec *vec = kh_value(outputs, k); + + kv_push(const char *, *vec, path); + return 1; +} + +void criterion_free_output(void) { + if (reporters) + kh_destroy(ht_str, reporters); + + if (outputs) { + for (khint_t k = kh_begin(outputs); k != kh_end(outputs); ++k) { + if (!kh_exist(outputs, k)) + continue; + str_vec *vec = kh_value(outputs, k); + kv_destroy(*vec); + free(vec); + } + kh_destroy(ht_path, outputs); + } +} + +void process_all_output(struct criterion_global_stats *stats) { + if (!outputs || !reporters) + return; + + for (khint_t k = kh_begin(reporters); k != kh_end(reporters); ++k) { + if (!kh_exist(reporters, k)) + continue; + + criterion_reporter *report = kh_value(reporters, k); + const char *name = kh_key(reporters, k); + khint_t ko = kh_get(ht_path, outputs, name); + if (ko == kh_end(outputs)) + continue; + + str_vec *vec = kh_value(outputs, ko); + for (size_t i = 0; i < kv_size(*vec); ++i) { + const char *path = kv_A(*vec, i); + + FILE *f; + if (path[0] == '-' && !path[1]) + f = stderr; + else + f = fopen(path, "w"); + + if (!f) { + int errno2 = errno; + criterion_perror(_(msg_err), path, name, strerror(errno2)); + continue; + } + + criterion_pinfo(CRITERION_PREFIX_DASHES, _(msg_ok), name, path); + report(f, stats); + } + } +} diff --git a/src/io/output.h b/src/io/output.h new file mode 100644 index 00000000..eccf24bf --- /dev/null +++ b/src/io/output.h @@ -0,0 +1,36 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef OUTPUT_H_ +# define OUTPUT_H_ + +# include "criterion/output.h" + +void process_all_output(struct criterion_global_stats *stats); +void criterion_free_output(void); + +void tap_report(FILE *f, struct criterion_global_stats *stats); +void xml_report(FILE *f, struct criterion_global_stats *stats); +void json_report(FILE *f, struct criterion_global_stats *stats); + +#endif /* !OUTPUT_H_ */ diff --git a/src/io/redirect.c b/src/io/redirect.c index a9d80d0d..2ee3e196 100644 --- a/src/io/redirect.c +++ b/src/io/redirect.c @@ -49,7 +49,7 @@ void cr_redirect_stdin(void) { FILE* cr_get_redirected_stdout(void) { static FILE *f; if (!f) { - f = pipe_in(stdout_redir, 0); + f = pipe_in(stdout_redir, PIPE_NOOPT); if (!f) cr_assert_fail("Could not get redirected stdout read end."); } @@ -59,7 +59,7 @@ FILE* cr_get_redirected_stdout(void) { FILE* cr_get_redirected_stderr(void) { static FILE *f; if (!f) { - f = pipe_in(stderr_redir, 0); + f = pipe_in(stderr_redir, PIPE_NOOPT); if (!f) cr_assert_fail("Could not get redirected stderr read end."); } @@ -69,7 +69,7 @@ FILE* cr_get_redirected_stderr(void) { FILE* cr_get_redirected_stdin(void) { static FILE *f; if (!f) { - f = pipe_out(stdin_redir, 0); + f = pipe_out(stdin_redir, PIPE_NOOPT); if (!f) cr_assert_fail("Could not get redirected stdin write end."); } diff --git a/src/log/tap.c b/src/io/tap.c similarity index 55% rename from src/log/tap.c rename to src/io/tap.c index 4615f707..6c7b7977 100644 --- a/src/log/tap.c +++ b/src/io/tap.c @@ -26,59 +26,36 @@ #include #include #include "criterion/stats.h" -#include "criterion/logging.h" #include "criterion/options.h" -#include "criterion/ordered-set.h" #include "compat/posix.h" +#include "compat/strtok.h" #include "compat/time.h" #include "config.h" #include "common.h" -#ifdef _MSC_VER -# define strdup _strdup -#endif - -void tap_log_pre_all(struct criterion_test_set *set) { - size_t enabled_count = 0; - FOREACH_SET(struct criterion_suite_set *s, set->suites) { - if ((s->suite.data && s->suite.data->disabled) || !s->tests) - continue; - - FOREACH_SET(struct criterion_test *test, s->tests) { - if (!test->data->disabled) - ++enabled_count; - } - } - criterion_important("TAP version 13\n1.." SIZE_T_FORMAT "\n", set->tests); - criterion_important("# Criterion v%s\n", VERSION); +static void print_prelude(FILE *f, struct criterion_global_stats *stats) { + fprintf(f, "TAP version 13\n1.." + CR_SIZE_T_FORMAT + "\n", stats->nb_tests); + fprintf(f, "# Criterion v%s\n", VERSION); } -void tap_log_pre_suite(struct criterion_suite_set *set) { - criterion_important("\n# Running " SIZE_T_FORMAT " tests from %s\n", - set->tests->size, - set->suite.name); +static void print_pre_suite(FILE *f, struct criterion_suite_stats *stats) { + fprintf(f, "\n# Running " + CR_SIZE_T_FORMAT + " tests from %s\n", + stats->nb_tests, + stats->suite->name); } static INLINE bool is_disabled(struct criterion_test *t, struct criterion_suite *s) { return t->data->disabled || (s->data && s->data->disabled); } -void tap_log_post_suite(struct criterion_suite_stats *stats) { - for (struct criterion_test_stats *ts = stats->tests; ts; ts = ts->next) { - if (is_disabled(ts->test, stats->suite)) { - criterion_important("ok - %s::%s %s # SKIP %s is disabled\n", - ts->test->category, - ts->test->name, - DEF(ts->test->data->description, ""), - ts->test->data->disabled ? "test" : "suite"); - } - } -} - -void tap_log_post_test(struct criterion_test_stats *stats) { +static void print_test_normal(FILE *f, struct criterion_test_stats *stats) { const char *format = can_measure_time() ? "%s - %s::%s %s (%3.2fs)\n" : "%s - %s::%s %s\n"; - criterion_important(format, + fprintf(f, format, stats->failed ? "not ok" : "ok", stats->test->category, stats->test->name, @@ -87,49 +64,64 @@ void tap_log_post_test(struct criterion_test_stats *stats) { for (struct criterion_assert_stats *asrt = stats->asserts; asrt; asrt = asrt->next) { if (!asrt->passed) { char *dup = strdup(*asrt->message ? asrt->message : ""); -#ifdef VANILLA_WIN32 - char *line = strtok(dup, "\n"); -#else char *saveptr = NULL; char *line = strtok_r(dup, "\n", &saveptr); -#endif bool sf = criterion_options.short_filename; - criterion_important(" %s:%u: Assertion failed: %s\n", + fprintf(f, " %s:%u: Assertion failed: %s\n", sf ? basename_compat(asrt->file) : asrt->file, asrt->line, line); -#ifdef VANILLA_WIN32 - while ((line = strtok(NULL, "\n"))) -#else + while ((line = strtok_r(NULL, "\n", &saveptr))) -#endif - criterion_important(" %s\n", line); + fprintf(f, " %s\n", line); free(dup); } } } -void tap_log_test_crash(struct criterion_test_stats *stats) { +static void print_test_crashed(FILE *f, struct criterion_test_stats *stats) { bool sf = criterion_options.short_filename; - criterion_important("not ok - %s::%s unexpected signal after %s:%u\n", + fprintf(f, "not ok - %s::%s unexpected signal after %s:%u\n", stats->test->category, stats->test->name, sf ? basename_compat(stats->file) : stats->file, stats->progress); } -void tap_log_test_timeout(struct criterion_test_stats *stats) { - criterion_important("not ok - %s::%s timed out (%3.2fs)\n", +static void print_test_timeout(FILE *f, struct criterion_test_stats *stats) { + fprintf(f, "not ok - %s::%s timed out (%3.2fs)\n", stats->test->category, stats->test->name, stats->elapsed_time); } -struct criterion_output_provider tap_logging = { - .log_pre_all = tap_log_pre_all, - .log_pre_suite = tap_log_pre_suite, - .log_test_crash = tap_log_test_crash, - .log_test_timeout = tap_log_test_timeout, - .log_post_test = tap_log_post_test, - .log_post_suite = tap_log_post_suite, -}; +static void print_test(FILE *f, + struct criterion_test_stats *ts, + struct criterion_suite_stats *ss) { + + if (is_disabled(ts->test, ss->suite)) { + fprintf(f, "ok - %s::%s %s # SKIP %s is disabled\n", + ts->test->category, + ts->test->name, + DEF(ts->test->data->description, ""), + ts->test->data->disabled ? "test" : "suite"); + } else if (ts->crashed) { + print_test_crashed(f, ts); + } else if (ts->timed_out) { + print_test_timeout(f, ts); + } else { + print_test_normal(f, ts); + } +} + +void tap_report(FILE *f, struct criterion_global_stats *stats) { + print_prelude(f, stats); + + for (struct criterion_suite_stats *ss = stats->suites; ss; ss = ss->next) { + print_pre_suite(f, ss); + + for (struct criterion_test_stats *ts = ss->tests; ts; ts = ts->next) { + print_test(f, ts, ss); + } + } +} diff --git a/src/io/xml.c b/src/io/xml.c new file mode 100644 index 00000000..a2cf30f7 --- /dev/null +++ b/src/io/xml.c @@ -0,0 +1,186 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define _GNU_SOURCE +#include +#include +#include +#include "criterion/stats.h" +#include "criterion/logging.h" +#include "criterion/options.h" +#include "criterion/internal/ordered-set.h" +#include "compat/posix.h" +#include "compat/strtok.h" +#include "compat/time.h" +#include "config.h" +#include "common.h" + +#define TESTSUITES_PROPERTIES \ + "name=\"Criterion Tests\" " \ + "tests=\"" CR_SIZE_T_FORMAT "\" " \ + "failures=\"" CR_SIZE_T_FORMAT "\" " \ + "errors=\"" CR_SIZE_T_FORMAT "\" " \ + "disabled=\"" CR_SIZE_T_FORMAT "\"" + +#define XML_BASE_TEMPLATE_BEGIN \ + "\n" \ + "\n" \ + "\n" \ + +#define XML_BASE_TEMPLATE_END \ + "\n" + +#define TESTSUITE_PROPERTIES \ + "name=\"%s\" " \ + "tests=\"" CR_SIZE_T_FORMAT "\" " \ + "failures=\"" CR_SIZE_T_FORMAT "\" " \ + "errors=\"" CR_SIZE_T_FORMAT "\" " \ + "disabled=\"" CR_SIZE_T_FORMAT "\" " \ + "skipped=\"" CR_SIZE_T_FORMAT "\"" + +#define XML_TESTSUITE_TEMPLATE_BEGIN \ + " \n" + +#define XML_TESTSUITE_TEMPLATE_END \ + " \n" + +#define TEST_PROPERTIES \ + "name=\"%s\" " \ + "assertions=\"" CR_SIZE_T_FORMAT "\" " \ + "status=\"%s\"" + +#define XML_TEST_TEMPLATE_BEGIN \ + " \n" \ + +#define XML_TEST_TEMPLATE_END \ + " \n" + +#define XML_TEST_SKIPPED " \n" + +#define LF " " + +#define XML_FAILURE_MSG_ENTRY \ + "%s:%u: %s" LF + +#define XML_TEST_FAILED_TEMPLATE_BEGIN \ + " " + +#define XML_TEST_FAILED_TEMPLATE_END \ + "\n" + +#define XML_CRASH_MSG_ENTRY \ + " " + +#define XML_TIMEOUT_MSG_ENTRY \ + " " + +static INLINE bool is_disabled(struct criterion_test *t, struct criterion_suite *s) { + return t->data->disabled || (s->data && s->data->disabled); +} + +static CR_INLINE +const char *get_status_string(struct criterion_test_stats *ts, + struct criterion_suite_stats *ss) { + + const char *status = "PASSED"; + if (ts->crashed || ts->timed_out) + status = "ERRORED"; + else if (ts->failed) + status = "FAILED"; + else if (is_disabled(ts->test, ss->suite)) + status = "SKIPPED"; + return status; +} + +static void print_test(FILE *f, + struct criterion_test_stats *ts, + struct criterion_suite_stats *ss) { + + fprintf(f, XML_TEST_TEMPLATE_BEGIN, + ts->test->name, + (size_t) (ts->passed_asserts + ts->failed_asserts), + get_status_string(ts, ss) + ); + + if (is_disabled(ts->test, ss->suite)) { + fprintf(f, XML_TEST_SKIPPED); + } else if (ts->crashed) { + fprintf(f, XML_CRASH_MSG_ENTRY); + } else if (ts->timed_out) { + fprintf(f, XML_TIMEOUT_MSG_ENTRY); + } else { + if (ts->failed) { + fprintf(f, XML_TEST_FAILED_TEMPLATE_BEGIN, ts->failed_asserts); + for (struct criterion_assert_stats *asrt = ts->asserts; asrt; asrt = asrt->next) { + if (!asrt->passed) { + bool sf = criterion_options.short_filename; + char *dup = strdup(*asrt->message ? asrt->message : ""); + char *saveptr = NULL; + char *line = strtok_r(dup, "\n", &saveptr); + + fprintf(f, XML_FAILURE_MSG_ENTRY, + sf ? basename_compat(asrt->file) : asrt->file, + asrt->line, + line + ); + + while ((line = strtok_r(NULL, "\n", &saveptr))) + fprintf(f, " %s" LF, line); + free(dup); + } + } + fprintf(f, XML_TEST_FAILED_TEMPLATE_END); + } + } + + fprintf(f, XML_TEST_TEMPLATE_END); +} + +void xml_report(FILE *f, struct criterion_global_stats *stats) { + fprintf(f, XML_BASE_TEMPLATE_BEGIN, + stats->nb_tests, + stats->tests_failed, + stats->tests_crashed, + stats->tests_skipped + ); + + for (struct criterion_suite_stats *ss = stats->suites; ss; ss = ss->next) { + + fprintf(f, XML_TESTSUITE_TEMPLATE_BEGIN, + ss->suite->name, + ss->nb_tests, + ss->tests_failed, + ss->tests_crashed, + ss->tests_skipped, + ss->tests_skipped + ); + + for (struct criterion_test_stats *ts = ss->tests; ts; ts = ts->next) { + print_test(f, ts, ss); + } + + fprintf(f, XML_TESTSUITE_TEMPLATE_END); + } + + fprintf(f, XML_BASE_TEMPLATE_END); +} diff --git a/src/log/logging.c b/src/log/logging.c index 7ef465b8..932337fc 100644 --- a/src/log/logging.c +++ b/src/log/logging.c @@ -31,8 +31,10 @@ #ifdef ENABLE_NLS # define LOG_FORMAT "[%1$s%2$s%3$s] %4$s" +# define ERROR_FORMAT "[%1$s%2$s%3$s] %4$s%5$s%6$s%7$s" #else # define LOG_FORMAT "[%s%s%s] %s" +# define ERROR_FORMAT "[%s%s%s] %s%s%s%s" #endif const struct criterion_prefix_data g_criterion_logging_prefixes[] = { @@ -42,6 +44,7 @@ const struct criterion_prefix_data g_criterion_logging_prefixes[] = { [CRITERION_LOGGING_PREFIX_SKIP] = { "SKIP", CRIT_FG_GOLD }, [CRITERION_LOGGING_PREFIX_PASS] = { "PASS", CRIT_FG_GREEN }, [CRITERION_LOGGING_PREFIX_FAIL] = { "FAIL", CRIT_FG_RED }, + [CRITERION_LOGGING_PREFIX_ERR] = { "ERR ", CRIT_FG_RED }, { NULL, NULL } }; @@ -56,11 +59,23 @@ void criterion_plog(enum criterion_logging_level level, const struct criterion_p vsnprintf(formatted_msg, sizeof formatted_msg, msg, args); va_end(args); - fprintf(stderr, _(LOG_FORMAT), + if (prefix == &g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_ERR]) { + fprintf(stderr, _(ERROR_FORMAT), CRIT_COLOR_NORMALIZE(prefix->color), prefix->prefix, - RESET, + CR_RESET, + CR_FG_RED, + CR_FG_BOLD, + formatted_msg, + CR_RESET); + } else { + fprintf(stderr, _(LOG_FORMAT), + CRIT_COLOR_NORMALIZE(prefix->color), + prefix->prefix, + CR_RESET, formatted_msg); + } + } void criterion_log(enum criterion_logging_level level, const char *msg, ...) { diff --git a/src/log/normal.c b/src/log/normal.c index 834c1827..f6221e09 100644 --- a/src/log/normal.c +++ b/src/log/normal.c @@ -29,22 +29,14 @@ #include "criterion/stats.h" #include "criterion/logging.h" #include "criterion/options.h" -#include "criterion/ordered-set.h" +#include "criterion/internal/ordered-set.h" #include "compat/posix.h" +#include "compat/strtok.h" #include "compat/time.h" #include "string/i18n.h" #include "config.h" #include "common.h" -#ifdef VANILLA_WIN32 -// fallback to strtok on windows since strtok_s is not available everywhere -# define strtok_r(str, delim, saveptr) strtok(str, delim) -#endif - -#ifdef _MSC_VER -# define strdup _strdup -#endif - typedef const char *const msg_t; static msg_t msg_pre_all = N_("Criterion v%s\n"); @@ -61,6 +53,7 @@ static msg_t msg_theory_fail = N_(" Theory %1$s::%2$s failed with the following static msg_t msg_test_timeout = N_("%1$s::%2$s: Timed out. (%3$3.2fs)\n"); static msg_t msg_test_crash_line = N_("%1$s%2$s%3$s:%4$s%5$u%6$s: Unexpected signal caught below this line!\n"); static msg_t msg_test_crash = N_("%1$s::%2$s: CRASH!\n"); +static msg_t msg_test_abort = N_("%1$s::%2$s: %3$s\n"); static msg_t msg_test_other_crash = N_("%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown.%4$s\n"); static msg_t msg_test_abnormal_exit = N_("%1$sWarning! The test `%2$s::%3$s` exited during its setup or teardown.%4$s\n"); static msg_t msg_pre_suite[] = N_s("Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n", @@ -77,10 +70,11 @@ static msg_t msg_post_test = "%s::%s\n"; static msg_t msg_post_suite_test = "%s::%s: Test is disabled\n"; static msg_t msg_post_suite_suite = "%s::%s: Suite is disabled\n"; static msg_t msg_assert_fail = "%s%s%s:%s%d%s: Assertion failed: %s\n"; -static msg_t msg_theory_fail = " Theory %s::%s failed with the following parameters: %s\n"; +static msg_t msg_theory_fail = " Theory %s::%s failed with the following parameters: (%s)\n"; static msg_t msg_test_timeout = "%s::%s: Timed out. (%3.2fs)\n"; static msg_t msg_test_crash_line = "%s%s%s:%s%u%s: Unexpected signal caught below this line!\n"; static msg_t msg_test_crash = "%s::%s: CRASH!\n"; +static msg_t msg_test_abort = N_("%s::%s: %s\n"); static msg_t msg_test_other_crash = "%sWarning! The test `%s::%s` crashed during its setup or teardown.%s\n"; static msg_t msg_test_abnormal_exit = "%sWarning! The test `%s::%s` exited during its setup or teardown.%s\n"; static msg_t msg_pre_suite[] = { "Running %s%lu%s test from %s%s%s:\n", @@ -92,7 +86,7 @@ static msg_t msg_post_all = "%sSynthesis: Tested: %s%lu%s " "%s\n"; #endif -void normal_log_pre_all(UNUSED struct criterion_test_set *set) { +void normal_log_pre_all(CR_UNUSED struct criterion_test_set *set) { criterion_pinfo(CRITERION_PREFIX_DASHES, _(msg_pre_all), VERSION); } @@ -148,37 +142,28 @@ void normal_log_post_all(struct criterion_global_stats *stats) { criterion_pimportant(CRITERION_PREFIX_EQUALS, _(msg_post_all), - FG_BOLD, - FG_BLUE, (unsigned long) tested, FG_BOLD, - FG_GREEN, (unsigned long) stats->tests_passed, FG_BOLD, - FG_RED, (unsigned long) stats->tests_failed, FG_BOLD, - FG_RED, (unsigned long) stats->tests_crashed, FG_BOLD, - RESET); + CR_FG_BOLD, + CR_FG_BLUE, (unsigned long) tested, CR_FG_BOLD, + CR_FG_GREEN, (unsigned long) stats->tests_passed, CR_FG_BOLD, + CR_FG_RED, (unsigned long) stats->tests_failed, CR_FG_BOLD, + CR_FG_RED, (unsigned long) stats->tests_crashed, CR_FG_BOLD, + CR_RESET); } void normal_log_assert(struct criterion_assert_stats *stats) { if (!stats->passed) { char *dup = strdup(*stats->message ? stats->message : ""); - -#ifdef VANILLA_WIN32 - char *line = strtok(dup, "\n"); -#else char *saveptr = NULL; char *line = strtok_r(dup, "\n", &saveptr); -#endif bool sf = criterion_options.short_filename; criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_assert_fail), - FG_BOLD, sf ? basename_compat(stats->file) : stats->file, RESET, - FG_RED, stats->line, RESET, + CR_FG_BOLD, sf ? basename_compat(stats->file) : stats->file, CR_RESET, + CR_FG_RED, stats->line, CR_RESET, line); -#ifdef VANILLA_WIN32 - while ((line = strtok(NULL, "\n"))) -#else while ((line = strtok_r(NULL, "\n", &saveptr))) -#endif criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_desc), line); free(dup); } @@ -188,30 +173,30 @@ void normal_log_test_crash(struct criterion_test_stats *stats) { bool sf = criterion_options.short_filename; criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_test_crash_line), - FG_BOLD, sf ? basename_compat(stats->file) : stats->file, RESET, - FG_RED, stats->progress, RESET); + CR_FG_BOLD, sf ? basename_compat(stats->file) : stats->file, CR_RESET, + CR_FG_RED, stats->progress, CR_RESET); criterion_pimportant(CRITERION_PREFIX_FAIL, _(msg_test_crash), stats->test->category, stats->test->name); } -void normal_log_other_crash(UNUSED struct criterion_test_stats *stats) { +void normal_log_other_crash(CR_UNUSED struct criterion_test_stats *stats) { criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_test_other_crash), - FG_BOLD, stats->test->category, stats->test->name, RESET); + CR_FG_BOLD, stats->test->category, stats->test->name, CR_RESET); } -void normal_log_abnormal_exit(UNUSED struct criterion_test_stats *stats) { +void normal_log_abnormal_exit(CR_UNUSED struct criterion_test_stats *stats) { criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_test_abnormal_exit), - FG_BOLD, stats->test->category, stats->test->name, RESET); + CR_FG_BOLD, stats->test->category, stats->test->name, CR_RESET); } void normal_log_pre_suite(struct criterion_suite_set *set) { criterion_pinfo(CRITERION_PREFIX_EQUALS, _s(msg_pre_suite[0], msg_pre_suite[1], set->tests->size), - FG_BLUE, (unsigned long) set->tests->size, RESET, - FG_GOLD, set->suite.name, RESET); + CR_FG_BLUE, (unsigned long) set->tests->size, CR_RESET, + CR_FG_GOLD, set->suite.name, CR_RESET); } void normal_log_theory_fail(struct criterion_theory_stats *stats) { @@ -222,7 +207,7 @@ void normal_log_theory_fail(struct criterion_theory_stats *stats) { stats->formatted_args); } -void normal_log_test_timeout(UNUSED struct criterion_test_stats *stats) { +void normal_log_test_timeout(CR_UNUSED struct criterion_test_stats *stats) { criterion_pimportant(CRITERION_PREFIX_FAIL, _(msg_test_timeout), stats->test->category, @@ -230,7 +215,25 @@ void normal_log_test_timeout(UNUSED struct criterion_test_stats *stats) { stats->elapsed_time); } -struct criterion_output_provider normal_logging = { +void normal_log_test_abort(CR_UNUSED struct criterion_test_stats *stats, const char *msg) { + + char *dup = strdup(msg); + char *saveptr = NULL; + char *line = strtok_r(dup, "\n", &saveptr); + + criterion_pimportant(CRITERION_PREFIX_DASHES, + _(msg_test_abort), + stats->test->category, + stats->test->name, + line); + + while ((line = strtok_r(NULL, "\n", &saveptr))) + criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_desc), line); + + free(dup); +} + +struct criterion_logger normal_logging = { .log_pre_all = normal_log_pre_all, .log_pre_init = normal_log_pre_init, .log_pre_suite = normal_log_pre_suite, @@ -238,6 +241,7 @@ struct criterion_output_provider normal_logging = { .log_theory_fail = normal_log_theory_fail, .log_test_timeout = normal_log_test_timeout, .log_test_crash = normal_log_test_crash, + .log_test_abort = normal_log_test_abort, .log_other_crash = normal_log_other_crash, .log_abnormal_exit = normal_log_abnormal_exit, .log_post_test = normal_log_post_test, diff --git a/src/string/extmatch.c b/src/string/extmatch.c index e6c75fc0..185a2640 100644 --- a/src/string/extmatch.c +++ b/src/string/extmatch.c @@ -27,7 +27,7 @@ #include #include -#include "criterion/common.h" +#include "criterion/internal/common.h" #include "common.h" struct context { @@ -99,8 +99,8 @@ typedef struct { char *str; } handler_arg; -static int active() { return 1; } -static int inactive() { return 0; } +static int active(CR_UNUSED struct context *ctx) { return 1; } +static int inactive(CR_UNUSED struct context *ctx) { return 0; } static int is_eos(struct context *ctx) { return peek_char(ctx) == '\0'; @@ -127,26 +127,26 @@ static inline void handle_special(struct context *ctx, handler_arg strs[5]) { } # define Handler(Name, ...) \ - static void Name(struct context *ctx, UNUSED char c) { \ + static void Name(struct context *ctx, CR_UNUSED char c) { \ handle_special(ctx, (handler_arg[5]) { \ __VA_ARGS__ \ }); \ } # define _ active -Handler(handle_plus, [POSTSUFFIX] = {_, "+"}, [ELSESTR] = {_, "+" }); -Handler(handle_star, [POSTSUFFIX] = {_, "*"}, [ELSESTR] = {_, ".*"}); -Handler(handle_wild, [POSTSUFFIX] = {_, "?"}, [ELSESTR] = {_, "." }); +Handler(handle_plus, [POSTSUFFIX] = {_, "+"}, [ELSESTR] = {_, "+" }) +Handler(handle_star, [POSTSUFFIX] = {_, "*"}, [ELSESTR] = {_, ".*"}) +Handler(handle_wild, [POSTSUFFIX] = {_, "?"}, [ELSESTR] = {_, "." }) Handler(handle_excl, [POSTPREFIX] = {_, "?!"}, [PRESUFFIX] = {is_eos, "$" }, [POSTSUFFIX] = {_, ".*"}, [ELSESTR] = {_, "!" } - ); -Handler(handle_at, [ELSESTR] = {_, "@"}); + ) +Handler(handle_at, [ELSESTR] = {_, "@"}) # undef _ -static void handle_rbra(struct context *ctx, UNUSED char c) { +static void handle_rbra(struct context *ctx, CR_UNUSED char c) { copy_char(ctx, c); if (peek_char(ctx) == '!') { read_char(ctx); @@ -159,7 +159,7 @@ static void escape_char(struct context *ctx, char c) { copy_char(ctx, c); } -static void escape_pipe(struct context *ctx, UNUSED char c) { +static void escape_pipe(struct context *ctx, CR_UNUSED char c) { if (ctx->depth == 0) copy_char(ctx, '\\'); copy_char(ctx, '|'); diff --git a/src/string/i18n.c b/src/string/i18n.c index c83a119c..da55950f 100644 --- a/src/string/i18n.c +++ b/src/string/i18n.c @@ -1,6 +1,6 @@ #include "i18n.h" #include "criterion/assert.h" -#include "criterion/asprintf-compat.h" +#include "criterion/internal/asprintf-compat.h" void init_i18n(void) { #if ENABLE_NLS @@ -8,7 +8,7 @@ void init_i18n(void) { #endif } -char *translate_assert_msg(int msg_index, ...) { +char *cr_translate_assert_msg(int msg_index, ...) { static char *messages[] = { [CRITERION_ASSERT_MSG_FAIL] = N_("The conditions for this assertion were not met."), [CRITERION_ASSERT_MSG_EXPR_FALSE] = N_("The expression %s is false."), diff --git a/src/string/i18n.h b/src/string/i18n.h index 827dc0a2..8613c9d2 100644 --- a/src/string/i18n.h +++ b/src/string/i18n.h @@ -1,3 +1,26 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #ifndef I18N_H_ # define I18N_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da638527..f23ac22e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,15 +1,27 @@ if (NOT MSVC) - set(CMAKE_C_FLAGS "-std=gnu99 -Wall -Wextra") - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEFAULT} -std=gnu99 -Wall -Wextra") + if (CMAKE_CXX_COMPILER_WORKS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEFAULT} ${CXX11_FLAG} -Wall -Wextra") + endif () endif () include_directories(../include ../src) set(TEST_SOURCES ordered-set.c +) + +if (THEORIES) + set(TEST_SOURCES ${TEST_SOURCES} asprintf.c + ) +endif () + +if (CMAKE_CXX_COMPILER_WORKS) + set(TEST_SOURCES ${TEST_SOURCES} redirect.cc -) + ) +endif () add_executable(criterion_unit_tests EXCLUDE_FROM_ALL ${TEST_SOURCES}) target_link_libraries(criterion_unit_tests criterion) diff --git a/test/asprintf.c b/test/asprintf.c index dd191aa0..f709bd0f 100644 --- a/test/asprintf.c +++ b/test/asprintf.c @@ -1,6 +1,6 @@ #include "criterion/criterion.h" #include "criterion/theories.h" -#include "criterion/asprintf-compat.h" +#include "criterion/internal/asprintf-compat.h" #include @@ -54,12 +54,3 @@ Theory((struct format_test *fmt), asprintf, valid) { free(actual); } - -#if defined(__unix__) && defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wformat" - -Test(asprintf, invalid) { - char *actual; - cr_expect_lt(cr_asprintf(&actual, "%"), 0); -} -#endif diff --git a/test/ordered-set.c b/test/ordered-set.c index 3ecdbf1b..92a4d28d 100644 --- a/test/ordered-set.c +++ b/test/ordered-set.c @@ -1,7 +1,7 @@ #include #include "criterion/criterion.h" -#include "criterion/ordered-set.h" +#include "criterion/internal/ordered-set.h" int compare_gt(void *a, void *b) { int *ia = a, *ib = b; diff --git a/test/redirect.cc b/test/redirect.cc index daa4fbf1..3afd0ac5 100644 --- a/test/redirect.cc +++ b/test/redirect.cc @@ -4,7 +4,7 @@ #include // set a timeout for I/O tests -TestSuite(redirect, .timeout = 0.1); +TestSuite(redirect, .timeout = 1); #if __GNUC__ >= 5 Test(redirect, mock) {