pax_global_header00006660000000000000000000000064136150404150014511gustar00rootroot0000000000000052 comment=2f719305afb0f44103734851cfe825e1b1d73dbf pmemkv-1.1/000077500000000000000000000000001361504041500126515ustar00rootroot00000000000000pmemkv-1.1/.clang-format000066400000000000000000000016561361504041500152340ustar00rootroot00000000000000AccessModifierOffset: -8 AlignOperands: false AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakTemplateDeclarations: true BasedOnStyle: LLVM BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBraces: Custom BreakStringLiterals: false ColumnLimit: 90 ConstructorInitializerAllOnOneLineOrOnePerLine: true ContinuationIndentWidth: 8 FixNamespaceComments: false IndentCaseLabels: true IndentWidth: 8 PointerAlignment: Right SpaceBeforeParens: ControlStatements SpacesBeforeTrailingComments: 1 SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false UseTab: Always pmemkv-1.1/.gitattributes000066400000000000000000000000721361504041500155430ustar00rootroot00000000000000* text=auto eol=lf *.jpg binary *.png binary *.gif binary pmemkv-1.1/.github/000077500000000000000000000000001361504041500142115ustar00rootroot00000000000000pmemkv-1.1/.github/workflows/000077500000000000000000000000001361504041500162465ustar00rootroot00000000000000pmemkv-1.1/.github/workflows/gha.yml000066400000000000000000000040061361504041500175300ustar00rootroot00000000000000# # The 'XXX_DISABLE_' suffix is used twice in this file to disable two actions: # 1) XXX_DISABLE_${CI_FILE_PUSH_IMAGE_TO_REPO} - disables pushing the rebuilt Docker image and # 2) XXX_DISABLE_AUTO_DOC_UPDATE - disables making pull requests with the update of documentation. # Those two actions are disabled, because they conflict with the same ones run on Travis. # Only one CI (Travis or GitHub Actions) can run them at the time, so they can be enabled here, # when we decide to switch from Travis to GitHub Actions. The 'XXX_DISABLE_' suffix should be removed then. # name: pmemkv on: [push, pull_request] env: REPO: pmemkv GITHUB_REPO: pmem/pmemkv DOCKERHUB_REPO: pmem/pmemkv jobs: linux: name: pmemkv runs-on: ubuntu-latest env: HOST_WORKDIR: /home/runner/work/pmemkv/pmemkv WORKDIR: utils/docker strategy: matrix: CONFIG: ["TYPE=normal OS=fedora OS_VER=31", "TYPE=normal OS=ubuntu OS_VER=19.10", "TYPE=normal OS=ubuntu OS_VER=19.10 COVERAGE=1", "TYPE=building OS=fedora OS_VER=31 COVERAGE=1 PUSH_IMAGE=1 XXX_DISABLE_AUTO_DOC_UPDATE=1", "TYPE=building OS=ubuntu OS_VER=19.10 COVERAGE=1 PUSH_IMAGE=1", "TYPE=bindings OS=ubuntu OS_VER=19.10_bindings PUSH_IMAGE=1", "TYPE=compatibility OS=fedora OS_VER=31", "TYPE=coverity OS=ubuntu OS_VER=19.10"] steps: - name: Clone the git repo uses: actions/checkout@v1 - name: Change ownership of the repo run: sudo chown -R 1000.1000 $HOST_WORKDIR - name: Pull or rebuild the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh - name: Push the image run: cd $WORKDIR && source ./set-vars.sh && ${{ matrix.CONFIG }} /bin/bash -c "if [[ -f XXX_DISABLE_${CI_FILE_PUSH_IMAGE_TO_REPO} ]]; then images/push-image.sh $OS-$OS_VER; fi" pmemkv-1.1/.gitignore000066400000000000000000000001741361504041500146430ustar00rootroot00000000000000.* !.gitignore !.gitattributes !.github/ !.travis.yml !.clang-format *.d /bin/ /build/ cmake-build-debug/ /googletest-*.zip pmemkv-1.1/.travis.yml000077700000000000000000000000001361504041500170102travis.ymlustar00rootroot00000000000000pmemkv-1.1/CMakeLists.txt000066400000000000000000000324161361504041500154170ustar00rootroot00000000000000# # Copyright 2017-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.10) project(pmemkv) include(cmake/helpers.cmake) set_version(VERSION) # set the default build type if(EXISTS "${CMAKE_SOURCE_DIR}/.git") set(DEFAULT_BUILD_TYPE "Debug") else() set(DEFAULT_BUILD_TYPE "RelWithDebInfo") endif() if(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to the default one (${DEFAULT_BUILD_TYPE})") set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose a type of build (Debug, Release or RelWithDebInfo)" FORCE) endif() option(BUILD_DOC "build documentation" ON) option(BUILD_EXAMPLES "build examples" ON) option(BUILD_TESTS "build tests" ON) option(TESTS_USE_VALGRIND "enable tests with valgrind (if found)" ON) option(BUILD_JSON_CONFIG "build the 'libpmemkv_json_config' library" ON) option(COVERAGE "run coverage test" OFF) if(COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -coverage") endif() # Each engine can be enabled separately. # By default all experimental engines are turned off. option(ENGINE_CMAP "enable cmap engine" ON) option(ENGINE_VCMAP "enable vcmap engine" ON) option(ENGINE_VSMAP "enable vsmap engine" ON) option(ENGINE_CACHING "enable experimental caching engine" OFF) option(ENGINE_STREE "enable experimental stree engine" OFF) option(ENGINE_TREE3 "enable experimental tree3 engine" OFF) option(DEVELOPER_MODE "enable developer's checks" OFF) option(CHECK_CPP_STYLE "check code style of C++ sources" OFF) # Do not treat include directories from the interfaces # of consumed Imported Targets as SYSTEM by default. set(CMAKE_NO_SYSTEM_FROM_IMPORTED 1) if(ENGINE_CMAP) add_definitions(-DENGINE_CMAP) message(STATUS "CMAP engine is ON") else() message(STATUS "CMAP engine is OFF") endif() if(ENGINE_VCMAP) add_definitions(-DENGINE_VCMAP) message(STATUS "VCMAP engine is ON") else() message(STATUS "VCMAP engine is OFF") endif() if(ENGINE_VSMAP) add_definitions(-DENGINE_VSMAP) message(STATUS "VSMAP engine is ON") else() message(STATUS "VSMAP engine is OFF") endif() if(ENGINE_CACHING) add_definitions(-DENGINE_CACHING) message(STATUS "CACHING engine is ON") else() message(STATUS "CACHING engine is OFF") endif() if(ENGINE_STREE) add_definitions(-DENGINE_STREE) message(STATUS "STREE engine is ON") else() message(STATUS "STREE engine is OFF") endif() if(ENGINE_TREE3) add_definitions(-DENGINE_TREE3) message(STATUS "TREE3 engine is ON") else() message(STATUS "TREE3 engine is OFF") endif() set(SOURCE_FILES src/libpmemkv.cc src/libpmemkv.h src/engine.cc src/engines/blackhole.cc src/engines/blackhole.h src/out.cc src/out.h ) # Add each engine source separately if(ENGINE_CMAP) list(APPEND SOURCE_FILES src/engines/cmap.h src/engines/cmap.cc ) endif() if(ENGINE_VCMAP) list(APPEND SOURCE_FILES src/engines/vcmap.h src/engines/vcmap.cc ) endif() if(ENGINE_VSMAP) list(APPEND SOURCE_FILES src/engines/vsmap.h src/engines/vsmap.cc ) endif() if(ENGINE_CACHING) list(APPEND SOURCE_FILES src/engines-experimental/caching.h src/engines-experimental/caching.cc ) endif() if(ENGINE_STREE) list(APPEND SOURCE_FILES src/engines-experimental/stree.h src/engines-experimental/stree.cc src/engines-experimental/stree/persistent_b_tree.h src/engines-experimental/stree/pstring.h ) endif() if(ENGINE_TREE3) list(APPEND SOURCE_FILES src/engines-experimental/tree3.h src/engines-experimental/tree3.cc ) endif() set(CXX_STANDARD 11 CACHE STRING "C++ language standard") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD ${CXX_STANDARD}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) set(LIBPMEMOBJ_CPP_REQUIRED_VERSION 1.9) set(LIBPMEMOBJ_REQUIRED_VERSION 1.8) set(MEMKIND_REQUIRED_VERSION 1.8.0) if(DEVELOPER_MODE) add_common_flag(-Werror) endif() add_common_flag(-Wall) add_common_flag(-Wpointer-arith) add_common_flag(-Wsign-compare) add_common_flag(-Wunreachable-code-return) add_common_flag(-Wmissing-variable-declarations) add_common_flag(-fno-common) add_common_flag(-Wunused-macros) add_common_flag(-Wsign-conversion) add_common_flag(-ggdb DEBUG) add_common_flag(-DDEBUG DEBUG) add_common_flag("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" RELEASE) find_package(PkgConfig QUIET) include(FindPerl) include(ExternalProject) include(FindThreads) include(CheckCXXSourceCompiles) include(GNUInstallDirs) set(PKG_CONFIG_REQUIRES) set(DEB_DEPENDS) set(RPM_DEPENDS) if(ENGINE_VSMAP OR ENGINE_VCMAP OR ENGINE_CMAP OR ENGINE_STREE OR ENGINE_TREE3) include(libpmemobj++) list(APPEND PKG_CONFIG_REQUIRES "libpmemobj++ >= ${LIBPMEMOBJ_CPP_REQUIRED_VERSION}") list(APPEND RPM_DEPENDS "libpmemobj >= ${LIBPMEMOBJ_REQUIRED_VERSION}") list(APPEND DEB_DEPENDS "libpmemobj1 (>= ${LIBPMEMOBJ_REQUIRED_VERSION}) | libpmemobj (>= ${LIBPMEMOBJ_REQUIRED_VERSION})") endif() if(ENGINE_VSMAP OR ENGINE_VCMAP) include(memkind) list(APPEND PKG_CONFIG_REQUIRES "memkind >= ${MEMKIND_REQUIRED_VERSION}") list(APPEND RPM_DEPENDS "memkind >= ${MEMKIND_REQUIRED_VERSION}") list(APPEND DEB_DEPENDS "libmemkind0 (>= ${MEMKIND_REQUIRED_VERSION})") endif() if(ENGINE_VCMAP) include(tbb) list(APPEND PKG_CONFIG_REQUIRES tbb) list(APPEND RPM_DEPENDS tbb) list(APPEND DEB_DEPENDS libtbb2) endif() if(ENGINE_CACHING) include(memcached-experimental) include(redis-experimental) list(APPEND PKG_CONFIG_REQUIRES libmemcached) list(APPEND RPM_DEPENDS libmemcached) list(APPEND DEB_DEPENDS libmemcached11) endif() string(REPLACE ";" " " PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES}") string(REPLACE ";" ", " RPM_PACKAGE_REQUIRES "${RPM_DEPENDS}") string(REPLACE ";" ", " DEB_PACKAGE_REQUIRES "${DEB_DEPENDS}") add_executable(check_license EXCLUDE_FROM_ALL utils/check_license/check-license.c) add_custom_target(checkers ALL) add_custom_target(cppstyle) add_custom_target(cppformat) add_custom_target(check-whitespace) add_custom_target(check-license COMMAND ${CMAKE_SOURCE_DIR}/utils/check_license/check-headers.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/check_license ${CMAKE_SOURCE_DIR}/LICENSE) add_dependencies(check-license check_license) add_custom_target(check-whitespace-main COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/check_whitespace ${CMAKE_SOURCE_DIR}/utils/check_license/*.sh ${CMAKE_SOURCE_DIR}/*.md) add_dependencies(check-whitespace check-whitespace-main) if(CHECK_CPP_STYLE) find_program(CLANG_FORMAT NAMES clang-format clang-format-9.0) set(CLANG_FORMAT_REQUIRED "9.0") if(CLANG_FORMAT) get_program_version_major_minor(${CLANG_FORMAT} CLANG_FORMAT_VERSION) if(NOT (CLANG_FORMAT_VERSION VERSION_EQUAL CLANG_FORMAT_REQUIRED)) message(FATAL_ERROR "required clang-format version is ${CLANG_FORMAT_REQUIRED} (version ${CLANG_FORMAT_VERSION} is installed)") endif() else() message(WARNING "clang-format not found - C++ sources will not be checked (required version: ${CLANG_FORMAT_REQUIRED})") endif() endif() if(DEVELOPER_MODE) if(NOT PERL_FOUND) message(FATAL_ERROR "Perl not found") endif() if (PERL_VERSION_STRING VERSION_LESS 5.16) message(FATAL_ERROR "Minimum required version of Perl is 5.16)") endif() execute_process(COMMAND ${PERL_EXECUTABLE} -MText::Diff -e "" ERROR_QUIET RESULT_VARIABLE PERL_TEXT_DIFF_STATUS) if (PERL_TEXT_DIFF_STATUS) message(FATAL_ERROR "Text::Diff Perl module not found (install libtext-diff-perl or perl-Text-Diff)") endif() add_dependencies(checkers cppstyle) add_dependencies(checkers check-whitespace) add_dependencies(checkers check-license) endif() add_library(pmemkv SHARED ${SOURCE_FILES}) set_target_properties(pmemkv PROPERTIES SOVERSION 1) target_link_libraries(pmemkv PRIVATE -Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libpmemkv.map) if(ENGINE_VSMAP OR ENGINE_VCMAP OR ENGINE_CMAP OR ENGINE_STREE OR ENGINE_TREE3) target_link_libraries(pmemkv PRIVATE ${LIBPMEMOBJ++_LIBRARIES}) endif() if(ENGINE_VSMAP OR ENGINE_VCMAP) target_link_libraries(pmemkv PRIVATE ${MEMKIND_LIBRARIES}) endif() if(ENGINE_VCMAP) target_link_libraries(pmemkv PRIVATE ${TBB_LIBRARIES}) endif() if(ENGINE_CACHING) target_link_libraries(pmemkv PRIVATE ${CMAKE_THREAD_LIBS_INIT} memcached) target_link_libraries(pmemkv PRIVATE acl_cpp protocol acl) endif() target_include_directories(pmemkv PRIVATE src/valgrind) # Enable libpmemobj-cpp valgrind annotations target_compile_options(pmemkv PRIVATE -DLIBPMEMOBJ_CPP_VG_ENABLED=1) if(BUILD_TESTS) if (TESTS_USE_VALGRIND) if(PKG_CONFIG_FOUND) pkg_check_modules(VALGRIND QUIET valgrind) else() find_package(VALGRIND QUIET) endif() if(NOT VALGRIND_FOUND) message(FATAL_ERROR "Valgrind not found, but flag TESTS_USE_VALGRIND was set.") endif() find_pmemcheck() if(NOT VALGRIND_PMEMCHECK_FOUND) message(WARNING "Valgrind pmemcheck not found. Some tests will be skipped.") elseif((NOT(PMEMCHECK_VERSION LESS 1.0)) AND PMEMCHECK_VERSION LESS 2.0) message(STATUS "Pmemcheck ${PMEMCHECK_VERSION} found") find_program(PMREORDER names pmreorder HINTS ${CMAKE_INSTALL_PREFIX}/bin) if(PMREORDER) message(STATUS "Pmreorder found") set(ENV{PATH} ${CMAKE_INSTALL_PREFIX}/bin:$ENV{PATH}) set(PMREORDER_SUPPORTED true CACHE INTERNAL "pmreorder support") endif() else() message(WARNING "Pmreorder will not be used and some tests will be skipped. " "Pmemcheck must be installed in version 1.x " "(currently installed version is ${PMEMCHECK_VERSION})") endif() endif() enable_testing() add_subdirectory(tests) endif() configure_file(libpmemkv.pc.in libpmemkv.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/libpmemkv.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) set_target_properties(pmemkv PROPERTIES PUBLIC_HEADER "src/libpmemkv.h;src/libpmemkv.hpp") if(BUILD_DOC) add_subdirectory(doc) endif() install(TARGETS pmemkv PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) if(BUILD_JSON_CONFIG) include(rapidjson) add_definitions(-DBUILD_JSON_CONFIG) set(SOURCE_FILES_JSON_CONFIG src/libpmemkv_json_config.cc src/libpmemkv_json_config.h src/out.cc src/out.h ) add_library(pmemkv_json_config SHARED ${SOURCE_FILES_JSON_CONFIG}) set_target_properties(pmemkv_json_config PROPERTIES SOVERSION 1) target_link_libraries(pmemkv_json_config PRIVATE pmemkv ${RapidJSON_LIBRARIES} -Wl,--version-script=${CMAKE_SOURCE_DIR}/src/libpmemkv_json_config.map) set_target_properties(pmemkv_json_config PROPERTIES PUBLIC_HEADER "src/libpmemkv_json_config.h") configure_file(libpmemkv_json_config.pc.in libpmemkv_json_config.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/libpmemkv_json_config.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install(TARGETS pmemkv_json_config PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() configure_file( "${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) add_cppstyle(src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/stree/*.h*) add_check_whitespace(src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/engines-experimental/stree/*.h*) if(BUILD_EXAMPLES) add_subdirectory(examples) endif() if(NOT "${CPACK_GENERATOR}" STREQUAL "") include(${CMAKE_SOURCE_DIR}/cmake/packages.cmake) endif() pmemkv-1.1/CONTRIBUTING.md000066400000000000000000000151601361504041500151050ustar00rootroot00000000000000# Contributing to pmemkv - [Opening New Issues](#opening-new-issues) - [Code Style](#code-style) - [Submitting Pull Requests](#submitting-pull-requests) - [Creating New Engines](#creating-new-engines) - [Creating Experimental Engines](#creating-experimental-engines) # Opening New Issues Please log bugs or suggestions as [GitHub issues](https://github.com/pmem/pmemkv/issues). Details such as OS and PMDK version are always appreciated. # Code Style * See `.clang-format` file in the repository for details * Indent with tabs (width: 8) * Max 90 chars per line * Space before '*' and '&' (rather than after) If you want to check and format your source code properly you can use CMake's `DEVELOPER_MODE` and `CHECK_CPP_STYLE` options. When enabled additional checks are switched on (cppstyle, whitespaces and headers). ```sh cmake .. -DDEVELOPER_MODE=ON -DCHECK_CPP_STYLE=ON ``` If you just want to format your code you can make adequate target: ```sh make cppformat ``` **NOTE**: We're using specific clang-format - version exactly **9.0** is required. # Submitting Pull Requests We take outside code contributions to `PMEMKV` through GitHub pull requests. **NOTE: If you do decide to implement code changes and contribute them, please make sure you agree your contribution can be made available under the [BSD-style License used for PMEMKV](LICENSE).** **NOTE: Submitting your changes also means that you certify the following:** ``` Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` In case of any doubt, the gatekeeper may ask you to certify the above in writing, i.e. via email or by including a `Signed-off-by:` line at the bottom of your commit comments. To improve tracking of who is the author of the contribution, we kindly ask you to use your real name (not an alias) when committing your changes to PMEMKV: ``` Author: Random J Developer ``` # Creating New Engines There are several motivations to create a `pmemkv` storage engine: * Using a new/different implementation strategy * Trying out a significant change to an existing engine * Creating a new version of an existing engine with some tweaks Next we'll walk you through the steps of creating a new engine. ### Picking Engine Name * Relatively short (users will have to type this!) * Formatted in all lower-case * No whitespace or special characters * Names should use common prefixes to denote capabilities: - prefix of "v" denotes volatile (persistent if omitted), appears first - prefix of "c" denotes concurrent (single-threaded if omitted), appears second - prefix of "s" denotes sorted (unsorted if omitted), appears last * For this example: `mytree` (persistent, single-threaded, unsorted) ### Creating Engine Header * Create `src/engines/mytree.h` header file * For new engines, use `blackhole.h` as a template * Define engine class in `pmem::kv` namespace * Use snake_case for implementation class name (`class my_tree`) ### Creating Engine Implementation * Create `src/engines/mytree.cc` implementation file * For new engines, use `blackhole.cc` as a template * Use `pmem::kv` namespace defined by the header ### Providing Unit Test * Create `tests/engines/mytree_test.cc` for unit tests * For new engines, use `blackhole_test.cc` as a template * For stable engines, just copy existing tests (eventually replace the original) ### Updating Build System * In `CMakeLists.txt`: * Add a build option for a new engine with a name like `ENGINE_MYTREE` and use it to ifdef all includes, dependencies and linking you may add * Add definition of the new option, like `-DENGINE_MYTREE`, so it can be used to ifdef engine-specific code (e.g. in `libpmemkv.cc`), like: ``` #ifdef ENGINE_MYTREE ... #endif ``` * Add `src/engines/mytree.h` and `src/engines/mytree.cc` to `SOURCE_FILES` * Use `pkg_check_modules` and/or `find_package` for upstream libraries * In `tests/CMakeLists.txt`: * Add `engines/mytree_test.cc` to `TEST_FILES` * CMake build and `make test` should complete without any errors now ### Updating Common Source * In `src/engine.cc`: * Add `#include "engines/mytree.h"` (within `#ifdef ENGINE_MYTREE` clause) * Update `create_engine` to return new `my_tree` instances * Build & verify engine now works with high-level bindings (see [README](README.md#language-bindings) for information on current bindings) ### Documentation * In `README.md`, link `mytree` in the table of supported engines * Update manpages in `doc` directory # Creating Experimental Engines The instructions above describe creating an engine that is considered stable. If you want, you can mark an engine as experimental and not include it in a build by default. Experimental engines are unsupported prototypes. These engines are still being actively developed, optimized, reviewed or documented, but they are still expected to have proper automated and passing tests. ### Experimental Code There are subdirectories `engines-experimental` (in `src` and `tests` directories) where all experimental source files should be placed. Whole engine-specific code (located in common source files) should be ifdef'd out using (newly) defined option (`ENGINE_MYTREE` in this case). ### Experimental Build Assets Extend existing section in `CMakeLists.txt` if your experimental engine requires libraries that are not yet included in the default build. ```cmake if(ENGINE_MYTREE) include(foo-experimental) endif() ``` As noted in the example above, the experimental CMake module should use `-experimental` suffix in the file name. ### Documentation * In `ENGINES-experimental.md`, add `mytree` section pmemkv-1.1/ChangeLog000066400000000000000000000055161361504041500144320ustar00rootroot00000000000000Fri Jan 31 2019 Szymon Romik * Version 1.1 This release introduces a defragmentation feature and cmap engine optimizations. Features: - API extension for defragmentation (currently supported only by cmap engine) Optimizations: - faster restart time of cmap engine - faster put method execution for cmap engine Mon Oct 28 2019 Szymon Romik * Version 1.0.1 This is a bugfix release for pmemkv 1.0 Major fixes: - fix finding memkind package - fix finding gtest in user-defined paths - add SOVERSION - skip valgrind and pmemcheck tests if they are not installed Fri Oct 04 2019 Szymon Romik * Version 1.0 This is the first release of pmemkv project which guarantees backward compatibility. Optimizations: - refactored core for libpmemobj-based engines - adjusted cmap engine to libpmemobj-cpp 1.8 optimizations Changes in tests and build system: - added support for generic tests parameterized i.a. by engine type - added framework for pmreorder tests Other changes: - added new, more specific error statuses - added C++ API for config structure - added doxygen documentation for C++ API - moved function to create configuration from JSON string to a new optional library - generic support for libpmemobj-based engines for handling persistent pointers as a config parameter Fri Jun 28 2019 Szymon Romik * Version 0.8 This is the first official release of pmemkv project. It unifies and extends native C and C++ API and introduces config structure - flexible way for configuring engines. Pmemkv core was redesigned - C++ API is implemented on the top of C API now and exposed as a header-only library. We have also provided extended pmemkv engines and API documentation in form of man pages. Optimizations: - string_view class for optimal keys passing - heterogeneous lookup in cmap engine Features: - added error messages - extended error handling Changes in tests and build system: - added Travis CI - cmake creation and build system refactoring - added tests with pmemcheck/memcheck/helgrind/drd - added clang-format - added Coverity support - added coverage support - added CI jobs for checking compatibility with Ruby, Java, Node.js bindings Others: - removed functions to iterate over keys only - removed engine_context function We have also modified existing engines and tests to meet changes in both pmemkv core and API. Poorly chosen function names were cleaned up. The reason this release has version 0.8 is because we are still open for suggestions from customers before we stabilize the APIs and commit to maintaining backward compatibility. It does not mean that the library is unfinished or unstable. However, more engines and extended functionality are planned to be delivered in the future. pmemkv-1.1/ENGINES-experimental.md000066400000000000000000000140601361504041500167570ustar00rootroot00000000000000# Experimental Storage Engines for pmemkv - [tree3](#tree3) - [stree](#stree) - [caching](#caching) # tree3 A persistent single-threaded engine, backed by a read-optimized B+ tree. It is disabled by default. It can be enabled in CMake using the `ENGINE_TREE3` option. ### Configuration Configuration must specify a `path` to a PMDK persistent pool, which can be a file (on a DAX filesystem), a DAX device, or a PMDK poolset file. * **path** -- Path to the database file + type: string * **force_create** -- If 0, pmemkv opens file specified by 'path', otherwise it creates it + type: uint64_t + default value: 0 * **size** -- Only needed when force_create is not 0, specifies size of the database [in bytes] + type: uint64_t + min value: 8388608 (8MB) ### Internals Internally, `tree3` uses a hybrid fingerprinted B+ tree implementation. Rather than keeping inner and leaf nodes of the tree in persistent memory, `tree3` uses a hybrid structure where inner nodes are kept in DRAM and leaf nodes only are kept in persistent memory. Though `tree3` has to recover all inner nodes when the engine is started, searches are performed in DRAM except for a final read from persistent memory. ![pmemkv-intro](https://cloud.githubusercontent.com/assets/913363/25543024/289f06d8-2c12-11e7-86e4-a1f0df891659.png) Leaf nodes in `tree3` contain multiple key-value pairs, indexed using 1-byte fingerprints ([Pearson hashes](https://en.wikipedia.org/wiki/Pearson_hashing)) that speed locating a given key. Leaf modifications are accelerated using [zero-copy updates](https://pmem.io/2017/03/09/pmemkv-zero-copy-leaf-splits.html). ### Prerequisites Libpmemobj-cpp package is required. # stree A persistent, single-threaded and sorted engine, backed by a B+ tree. It is disabled by default. It can be enabled in CMake using the `ENGINE_STREE` option. ### Configuration * **path** -- Path to the database file + type: string * **force_create** -- If 0, pmemkv opens file specified by 'path', otherwise it creates it + type: uint64_t + default value: 0 * **size** -- Only needed when force_create is not 0, specifies size of the database [in bytes] + type: uint64_t ### Internals (TBD) ### Prerequisites Libpmemobj-cpp package is required. # caching This engine is using a sub engine from the list above to cache requests to external Redis or Memcached server. It is disabled by default. It can be enabled in CMake using the `ENGINE_CACHING` option. ### Configuration Caching engine itself requires server connection settings. Part of the config required for the sub engine should be relevant to chosen engine. * **host** -- Server's IP + type: string * **port** -- Server's port + type: int64_t * **attempts** -- Number of connection attempts + type: int64_t * **ttl** -- Time to live [in seconds] + type: int64_t + default value: 0 * **remote_type** -- Server's type (Redis or Memcached) + type: string * **remote_user** -- Connection's user + type: string * **remote_pwd** -- User's password + type: string * **remote_url** -- Remote (server's) URL + type: string * **subengine** -- Config object for sub engine with its required settings + type: object ### Internals (TBD) ### Prerequisites Memcached and libacl ([see here for installation guide](INSTALLING.md#using-experimental-engines)) packages are required. ### Related Work --------- **pmse** `tree3` has a lot in common with [pmse](https://github.com/pmem/pmse) -- both implementations rely on PMDK internally, although they expose different APIs externally. Both `pmse` and `tree3` are based on a B+ tree implementation. The biggest difference is that the `pmse` tree keeps inner and leaf nodes in persistent memory, where `tree3` keeps inner nodes in DRAM and leaf nodes in persistent memory. (This means that `tree3` has to recover all inner nodes when the engine is started) **FPTree** This research paper describes a hybrid DRAM/NVM tree design (similar to the `tree3` storage engine) but this paper doesn't provide any code, and omits certain important implementation details. Beyond providing a clean-room implementation, the design of `tree3` differs from FPTree in several important areas: 1. `tree3` is written using PMDK C++ bindings, which exerts influence on its design and implementation. `tree3` uses generic PMDK transactions (i.e. `transaction::run()` closures), there is no need for micro-logging structures as described in the FPTree paper to make internal delete and split operations safe. `tree3` also adjusts sizes of data structures (to fit PMDK primitive types) for best cache-line optimization. 2. FPTree does not specify a hash method implementation, where `tree3` uses a Pearson hash (RFC 3074). 3. Within its persistent leaves, FPTree uses an array of key hashes with a separate visibility bitmap to track what hash slots are occupied. `tree3` takes a different approach and uses key hashes themselves to track visibility. This relies on a specially modified Pearson hash function, where a hash value of zero always indicates the slot is unused. This optimization eliminates the cost of using and maintaining visibility bitmaps as well as cramming more hashes into a single cache-line, and affects the implementation of every primitive operation in the tree. 4. `tree3` caches key hashes in DRAM (in addition to storing these as part of the persistent leaf). This speeds leaf operations, especially with slower media, for what seems like an acceptable rise in DRAM usage. 5. Within its persistent leaves, `tree3` combines hash, key and value into a single slot type (`KVSlot`). This leads to improved leaf split performance and reduced write amplification, since splitting can be performed by swapping pointers to slots without copying any key or value data stored in the slots. `KVSlot` internally stores key and value to a single persistent buffer, which minimizes the number of persistent allocations and improves storage efficiency with larger keys and values. **cpp_map** Use of PMDK C++ bindings by `tree3` was lifted from this example program. Many thanks to [@tomaszkapela](https://github.com/tomaszkapela) for providing a great example to follow! pmemkv-1.1/INSTALLING.md000066400000000000000000000153311361504041500146420ustar00rootroot00000000000000# Installing pmemkv Key/Value Datastore for Persistent Memory *This is experimental pre-release software and should not be used in production systems. APIs and file formats may change at any time without preserving backwards compatibility. All known issues and limitations are logged as GitHub issues.* ## Contents - [Building from Sources](#building-from-sources) - [Installing on Fedora](#installing-on-fedora) - [Installing on Ubuntu](#installing-on-ubuntu) - [Using Experimental Engines](#using-experimental-engines) - [Building packages](#building-packages) - [Using a Pool Set](#using-a-pool-set) ## Building from Sources **Prerequisites** * 64-bit Linux (OSX and Windows are not yet supported) * [PMDK](https://github.com/pmem/pmdk) - Persistent Memory Development Kit 1.8 * [libpmemobj-cpp](https://github.com/pmem/libpmemobj-cpp) - C++ bindings 1.9 for PMDK (required by all engines except blackhole and caching) * [memkind](https://github.com/memkind/memkind) - Volatile memory manager 1.8.0 (required by vsmap & vcmap engines) * [TBB](https://github.com/01org/tbb) - Thread Building Blocks (required by vcmap engine) * [RapidJSON](https://github.com/tencent/rapidjson) - JSON parser (required by `libpmemkv_json_config` helper library) * Used only for development & testing: * [GoogleTest](https://github.com/google/googletest) - test framework, version >= 1.8 * [valgrind](https://github.com/pmem/valgrind) - tool for profiling and memory leak detection. *pmem* forked version with *pmemcheck* tool is recommended, but upstream/original [valgrind](https://valgrind.org/) is also compatible (package valgrind-devel is required). * [pandoc](https://pandoc.org/) - markup converter to generate manpages * [doxygen](http://www.doxygen.nl/) - tool for generating documentation from annotated C++ sources * [graphviz](https://www.graphviz.org/) - graph visualization software required by _doxygen_ * [perl](https://www.perl.org/) - for whitespace checker script * [clang format](https://clang.llvm.org/docs/ClangFormat.html) - to format and check coding style, version 9.0 is required **Building pmemkv and running tests** ```sh git clone https://github.com/pmem/pmemkv cd pmemkv mkdir ./build cd ./build cmake .. # run CMake make # build everything make test # run all tests ``` Instead of the last command (`make test`) you can run ```sh ctest --output-on-failure ``` to see the output of failed tests. Building of the `libpmemkv_json_config` helper library is enabled by default. If you want to disable it (for example to get rid of the RapidJSON dependency) run: ```sh cmake .. -DBUILD_JSON_CONFIG=OFF ``` instead of: ```sh cmake .. ``` **Managing shared library** To package `pmemkv` as a shared library and install on your system: ```sh sudo make install # install shared library to the default location: /usr/local sudo make uninstall # remove shared library and headers ``` To install this library into other locations, pass appropriate value to cmake using CMAKE_INSTALL_PREFIX variable like this: ```sh cmake .. -DCMAKE_INSTALL_PREFIX=/usr sudo make install # install to path specified by CMAKE_INSTALL_PREFIX sudo make uninstall # remove shared library and headers from path specified by CMAKE_INSTALL_PREFIX ``` **Out-of-source builds** If the standard build does not suit your needs, create your own out-of-source build and run tests like this: ```sh cd ~ mkdir mybuild cd mybuild cmake ~/pmemkv # this directory should contain the source code of pmemkv make make test # or 'ctest --output-on-failure' ``` ## Installing on Fedora Install required packages: ```sh su -c 'dnf install autoconf automake cmake daxctl-devel gcc gcc-c++ \ libtool ndctl-devel numactl-devel rapidjson-devel tbb-devel' ``` Configure for proxy if necessary: ```sh git config --global http.proxy export HTTP_PROXY="" export HTTPS_PROXY="" ``` Install latest PMDK: ```sh cd ~ git clone https://github.com/pmem/pmdk cd pmdk make -j8 su -c 'make install' export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig ``` Install latest PMDK C++ bindings: ```sh cd ~ git clone https://github.com/pmem/libpmemobj-cpp cd libpmemobj-cpp mkdir build cd build cmake .. make su -c 'make install' ``` Install latest memkind: ```sh cd ~ git clone https://github.com/memkind/memkind cd memkind ./autogen.sh ./configure make su -c 'make install' ``` Finally [build and install pmemkv from sources](#building-from-sources). ## Installing on Ubuntu Install required packages: ```sh sudo apt install autoconf automake build-essential cmake libdaxctl-dev \ libndctl-dev libnuma-dev libtbb-dev libtool rapidjson-dev ``` Configure for proxy if necessary: ```sh git config --global http.proxy export HTTP_PROXY="" export HTTPS_PROXY="" ``` Install latest PMDK: ```sh cd ~ git clone https://github.com/pmem/pmdk cd pmdk make -j8 sudo make install ``` Install latest PMDK C++ bindings: ```sh cd ~ git clone https://github.com/pmem/libpmemobj-cpp cd libpmemobj-cpp mkdir build cd build cmake .. make sudo make install ``` Install latest memkind: ```sh cd ~ git clone https://github.com/memkind/memkind cd memkind ./autogen.sh ./configure make sudo make install ``` Finally [build and install pmemkv from sources](#building-from-sources). ## Using Experimental Engines To enable experimental engine(s) use adequate CMake parameter, e.g.: ```sh cmake .. -DENGINE_CACHING=ON ``` Now build will contain selected experimental engine(s) and their dependencies, that are not available by default. Additional libraries (not listed above, in prerequisites section) are required only by caching engine. If you want to use it you need to follow these instructions: First build and install `pmemkv` as described above. Then, install client libraries for Memcached: ```sh cd ~ mkdir work cd work wget https://launchpad.net/libmemcached/1.0/0.21/+download/libmemcached-0.21.tar.gz tar -xvf libmemcached-0.21.tar.gz mv libmemcached-0.21 libmemcached cd libmemcached ./configure make su -c 'make install' ``` Install client libraries for Redis: ```sh cd ~ mkdir work cd work git clone https://github.com/acl-dev/acl.git mv acl libacl cd libacl/lib_acl_cpp make cd ../lib_acl make cd ../lib_protocol make ``` ## Building packages ```sh ... cmake .. -DCPACK_GENERATOR="$GEN" -DCMAKE_INSTALL_PREFIX=/usr make package ``` $GEN is a type of package generator and can be RPM or DEB. CMAKE_INSTALL_PREFIX must be set to a destination where packages will be installed. ## Using a Pool Set First create a pool set descriptor (`~/pmemkv.poolset` in this example): ``` PMEMPOOLSET 1000M /dev/shm/pmemkv1 1000M /dev/shm/pmemkv2 ``` Next initialize the pool set: ```sh pmempool create --layout pmemkv obj ~/pmemkv.poolset ``` pmemkv-1.1/LICENSE000066400000000000000000000033761361504041500136670ustar00rootroot00000000000000Copyright 2017-2019, Intel Corporation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. Everything in this source tree is covered by the previous license with the following exceptions: * src/valgrind/valgrind.h, src/valgrind/memcheck.h, src/valgrind/helgrind.h, src/valgrind/drd.h are covered by another similar BSD license variant, contained in those files. pmemkv-1.1/README.md000066400000000000000000000100761361504041500141340ustar00rootroot00000000000000# pmemkv [![Build Status](https://travis-ci.org/pmem/pmemkv.svg?branch=master)](https://travis-ci.org/pmem/pmemkv) [![PMEMKV version](https://img.shields.io/github/tag/pmem/pmemkv.svg)](https://github.com/pmem/pmemkv/releases/latest) [![Coverity Scan Build Status](https://scan.coverity.com/projects/18408/badge.svg)](https://scan.coverity.com/projects/pmem-pmemkv) [![Coverage Status](https://codecov.io/github/pmem/pmemkv/coverage.svg?branch=master)](https://codecov.io/gh/pmem/pmemkv/branch/master) Key/Value Datastore for Persistent Memory ## Overview `pmemkv` is a local/embedded key-value datastore optimized for persistent memory. Rather than being tied to a single language or backing implementation, `pmemkv` provides different options for language bindings and storage engines. For more information, see https://pmem.io/pmemkv. The C++ API of pmemkv is documented in the Doxygen documentation listed below: - [master](https://pmem.io/pmemkv/master/doxygen/index.html) - [v1.1](https://pmem.io/pmemkv/v1.1/doxygen/index.html) - [v1.0](https://pmem.io/pmemkv/v1.0/doxygen/index.html) There is also a small helper library `pmemkv_json_config` provided. See its [manual](doc/libpmemkv_json_config.3.md) for details. - [Installation](#installation) - [Language Bindings](#language-bindings) - [Storage Engines](#storage-engines) - [Tools and Utilities](#tools-and-utilities) ## Installation [Installation guide](INSTALLING.md) provides detailed instructions how to build and install `pmemkv` from sources, build rpm and deb packages and explains usage of experimental engines and pool sets. - [Building from Sources](INSTALLING.md#building-from-sources) - [Installing on Fedora](INSTALLING.md#installing-on-fedora) - [Installing on Ubuntu](INSTALLING.md#installing-on-ubuntu) - [Using Experimental Engines](INSTALLING.md#using-experimental-engines) - [Building packages](INSTALLING.md#building-packages) - [Using a Pool Set](INSTALLING.md#using-a-pool-set) ## Language Bindings `pmemkv` is written in C/C++ and it is used by bindings for Java, Node.js, Python, and Ruby applications. ![pmemkv-bindings](https://user-images.githubusercontent.com/12031346/65962933-ff6bfc00-e459-11e9-9552-d6326e9c0684.png) ### C/C++ Examples Examples for C and C++ can be found within this repository in [examples directory](./examples/). ### Other Languages Abovementioned bindings are maintained in separate GitHub repositories, but are still kept in sync with the main `pmemkv` distribution. * Java - https://github.com/pmem/pmemkv-java * \+ Java Native Interface - https://github.com/pmem/pmemkv-jni * Node.js - https://github.com/pmem/pmemkv-nodejs * Python - https://github.com/pmem/pmemkv-python * Ruby - https://github.com/pmem/pmemkv-ruby ## Storage Engines `pmemkv` provides multiple storage engines that conform to the same common API, so every engine can be used with all language bindings and utilities. Engines are loaded by name at runtime. | Engine Name | Description | Experimental? | Concurrent? | Sorted? | | ------------ | ----------- | ------------- | ----------- | ------- | | [blackhole](doc/libpmemkv.7.md#blackhole) | Accepts everything, returns nothing | No | Yes | No | | [cmap](doc/libpmemkv.7.md#cmap) | Concurrent hash map | No | Yes | No | | [vsmap](doc/libpmemkv.7.md#vsmap) | Volatile sorted hash map | No | No | Yes | | [vcmap](doc/libpmemkv.7.md#vcmap) | Volatile concurrent hash map | No | Yes | No | | [tree3](ENGINES-experimental.md#tree3) | Persistent B+ tree | Yes | No | No | | [stree](ENGINES-experimental.md#stree) | Sorted persistent B+ tree | Yes | No | Yes | | [caching](ENGINES-experimental.md#caching) | Caching for remote Memcached or Redis server | Yes | No | - | The production quality engines are described in the [libpmemkv(7)](doc/libpmemkv.7.md#engines) manual and the experimental engines are described in the [ENGINES-experimental.md](ENGINES-experimental.md) file. [Contributing a new engine](CONTRIBUTING.md#creating-new-engines) is easy and encouraged! ## Tools and Utilities Benchmarks' scripts and other helpful utilities are available here: https://github.com/pmem/pmemkv-tools pmemkv-1.1/VERSION000066400000000000000000000000031361504041500137120ustar00rootroot000000000000001.1pmemkv-1.1/cmake/000077500000000000000000000000001361504041500137315ustar00rootroot00000000000000pmemkv-1.1/cmake/cmake_uninstall.cmake.in000066400000000000000000000020501361504041500205060ustar00rootroot00000000000000# From: https://gitlab.kitware.com/cmake/community/wikis/FAQ if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) pmemkv-1.1/cmake/helpers.cmake000066400000000000000000000161011361504041500163740ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) function(set_version VERSION) set(VERSION_FILE ${CMAKE_SOURCE_DIR}/VERSION) if(EXISTS ${VERSION_FILE}) file(READ ${VERSION_FILE} FILE_VERSION) string(REPLACE "[\r\n]" "" FILE_VERSION ${FILE_VERSION}) string(STRIP ${FILE_VERSION} FILE_VERSION) set(VERSION ${FILE_VERSION} PARENT_SCOPE) return() endif() execute_process(COMMAND git describe OUTPUT_VARIABLE GIT_VERSION) if(GIT_VERSION) # 1.5-rc1-19-gb8f78a329 -> 1.5-rc1.git19.gb8f78a329 string(REGEX MATCHALL "([0-9.]*)-rc([0-9]*)-([0-9]*)-([0-9a-g]*)" MATCHES ${GIT_VERSION}) if(MATCHES) set(VERSION "${CMAKE_MATCH_1}-rc${CMAKE_MATCH_2}.git${CMAKE_MATCH_3}.${CMAKE_MATCH_4}" PARENT_SCOPE) return() endif() # 1.5-19-gb8f78a329 -> 1.5-git19.gb8f78a329 string(REGEX MATCHALL "([0-9.]*)-([0-9]*)-([0-9a-g]*)" MATCHES ${GIT_VERSION}) if(MATCHES) set(VERSION "${CMAKE_MATCH_1}-git${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) endif() else() execute_process(COMMAND git log -1 --format=%h OUTPUT_VARIABLE GIT_COMMIT) set(VERSION ${GIT_COMMIT} PARENT_SCOPE) endif() endfunction() function(find_pmemcheck) set(ENV{PATH} ${VALGRIND_PREFIX}/bin:$ENV{PATH}) execute_process(COMMAND valgrind --tool=pmemcheck --help RESULT_VARIABLE VALGRIND_PMEMCHECK_RET OUTPUT_QUIET ERROR_QUIET) if(VALGRIND_PMEMCHECK_RET) set(VALGRIND_PMEMCHECK_FOUND 0 CACHE INTERNAL "") else() set(VALGRIND_PMEMCHECK_FOUND 1 CACHE INTERNAL "") endif() if(VALGRIND_PMEMCHECK_FOUND) execute_process(COMMAND valgrind --tool=pmemcheck true ERROR_VARIABLE PMEMCHECK_OUT OUTPUT_QUIET) string(REGEX MATCH ".*pmemcheck-([0-9.]+),.*" PMEMCHECK_OUT "${PMEMCHECK_OUT}") set(PMEMCHECK_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "") endif() endfunction() # Generates cppstyle-$name and cppformat-$name targets and attaches them # as dependencies of global "cppformat" target. # cppstyle-$name target verifies C++ style of files in current source dir. # cppformat-$name target reformats files in current source dir. # If more arguments are used, then they are used as files to be checked # instead. # ${name} must be unique. function(add_cppstyle name) if(NOT CLANG_FORMAT) return() endif() if(${ARGC} EQUAL 1) add_custom_target(cppstyle-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} format ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ) else() add_custom_target(cppstyle-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${ARGN} ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} format ${ARGN} ) endif() add_dependencies(cppstyle cppstyle-${name}) add_dependencies(cppformat cppformat-${name}) endfunction() # Generates check-whitespace-$name target and attaches it as a dependency # of global "check-whitespace" target. # ${name} must be unique. function(add_check_whitespace name) if(NOT DEVELOPER_MODE) return() endif() add_custom_target(check-whitespace-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/check_whitespace ${ARGN}) add_dependencies(check-whitespace check-whitespace-${name}) endfunction() # Sets ${ret} to version of program specified by ${name} in major.minor format function(get_program_version_major_minor name ret) execute_process(COMMAND ${name} --version OUTPUT_VARIABLE cmd_ret ERROR_QUIET) STRING(REGEX MATCH "([0-9]+.)([0-9]+.)" VERSION ${cmd_ret}) SET(${ret} ${VERSION} PARENT_SCOPE) endfunction() # prepends prefix to list of strings function(prepend var prefix) set(listVar "") foreach(f ${ARGN}) list(APPEND listVar "${prefix}/${f}") endforeach(f) set(${var} "${listVar}" PARENT_SCOPE) endfunction() # Checks whether flag is supported by current C++ compiler and appends # it to the relevant cmake variable. # 1st argument is a flag # 2nd (optional) argument is a build type (debug, release) macro(add_cxx_flag flag) string(REPLACE - _ flag2 ${flag}) string(REPLACE " " _ flag2 ${flag2}) string(REPLACE = "_" flag2 ${flag2}) set(check_name "CXX_HAS_${flag2}") check_cxx_compiler_flag(${flag} ${check_name}) if (${${check_name}}) if (${ARGC} EQUAL 1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") else() set(CMAKE_CXX_FLAGS_${ARGV1} "${CMAKE_CXX_FLAGS_${ARGV1}} ${flag}") endif() endif() endmacro() # Checks whether flag is supported by current C compiler and appends # it to the relevant cmake variable. # 1st argument is a flag # 2nd (optional) argument is a build type (debug, release) macro(add_c_flag flag) string(REPLACE - _ flag2 ${flag}) string(REPLACE " " _ flag2 ${flag2}) string(REPLACE = "_" flag2 ${flag2}) set(check_name "C_HAS_${flag2}") check_c_compiler_flag(${flag} ${check_name}) if (${${check_name}}) if (${ARGC} EQUAL 1) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") else() set(CMAKE_C_FLAGS_${ARGV1} "${CMAKE_C_FLAGS_${ARGV1}} ${flag}") endif() endif() endmacro() # Checks whether flag is supported by both C and C++ compiler and appends # it to the relevant cmake variables. # 1st argument is a flag # 2nd (optional) argument is a build type (debug, release) macro(add_common_flag flag) add_c_flag(${flag} ${ARGV1}) add_cxx_flag(${flag} ${ARGV1}) endmacro() pmemkv-1.1/cmake/libpmemobj++.cmake000066400000000000000000000036271361504041500172110ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++>=${LIBPMEMOBJ_CPP_REQUIRED_VERSION}) else() find_package(LIBPMEMOBJ++ ${LIBPMEMOBJ_CPP_REQUIRED_VERSION} REQUIRED libpmemobj++) message(STATUS "libpmemobj++ found the old way (w/o pkg-config)") endif() include_directories(${LIBPMEMOBJ++_INCLUDE_DIRS}) link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) pmemkv-1.1/cmake/memcached-experimental.cmake000066400000000000000000000032471361504041500213420ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # FIXME hardcoded path is a VERY bad idea set(MEMCACHED_INCLUDE $ENV{HOME}/work/libmemcached) include_directories(${MEMCACHED_INCLUDE}) pmemkv-1.1/cmake/memkind.cmake000066400000000000000000000060271361504041500163640ustar00rootroot00000000000000# Copyright 2017-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. if(PKG_CONFIG_FOUND) pkg_check_modules(MEMKIND memkind>=${MEMKIND_REQUIRED_VERSION}) endif() if(NOT MEMKIND_FOUND) unset(MEMKIND_LIBRARY CACHE) unset(MEMKIND_INCLUDEDIR CACHE) # try old method include(FindPackageHandleStandardArgs) find_path(MEMKIND_INCLUDEDIR pmem_allocator.h) find_library(MEMKIND_LIBRARY NAMES memkind libmemkind) mark_as_advanced(MEMKIND_LIBRARY MEMKIND_INCLUDEDIR) find_package_handle_standard_args(MEMKIND DEFAULT_MSG MEMKIND_INCLUDEDIR MEMKIND_LIBRARY) if(MEMKIND_FOUND) set(MEMKIND_LIBRARIES ${MEMKIND_LIBRARY}) set(MEMKIND_INCLUDE_DIRS ${MEMKIND_INCLUDEDIR}) message(STATUS "Memkind library found the old way (w/o pkg-config)") else() message(FATAL_ERROR "Memkind library (>=${MEMKIND_REQUIRED_VERSION}) not found") endif() endif() link_directories(${MEMKIND_LIBRARY_DIRS}) include_directories(${MEMKIND_INCLUDE_DIRS}) # XXX To be removed when memkind updated to ver. >= 1.10 # Check if libmemkind namespace is available, if not # the old namespace will be used for 'pmem::allocator'. # ref: https://github.com/pmem/pmemkv/issues/429 set(SAVED_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_INCLUDES ${MEMKIND_INCLUDE_DIRS}) CHECK_CXX_SOURCE_COMPILES( "#include \"pmem_allocator.h\" int main(void) { libmemkind::pmem::allocator *alc = nullptr; (void)alc; }" LIBMEMKIND_NAMESPACE_PRESENT) set(CMAKE_REQUIRED_INCLUDES ${SAVED_CMAKE_REQUIRED_INCLUDES}) if(LIBMEMKIND_NAMESPACE_PRESENT) add_definitions(-DUSE_LIBMEMKIND_NAMESPACE) endif() pmemkv-1.1/cmake/packages.cmake000066400000000000000000000077511361504041500165230ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # packages.cmake - CPack configuration for rpm and deb generation # string(TOUPPER "${CPACK_GENERATOR}" CPACK_GENERATOR) if(NOT ("${CPACK_GENERATOR}" STREQUAL "DEB" OR "${CPACK_GENERATOR}" STREQUAL "RPM")) message(FATAL_ERROR "Wrong CPACK_GENERATOR value, valid generators are: DEB, RPM") endif() set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") set(CMAKE_INSTALL_TMPDIR /tmp CACHE PATH "Output dir for tmp") set(CPACK_COMPONENTS_ALL_IN_ONE) # Filter out some of directories from %dir section, which are expected # to exist in filesystem. Leaving them might lead to conflicts with other # packages (for example with 'filesystem' package on fedora which specify # /usr, /usr/local, etc.) set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR} ${CPACK_PACKAGING_INSTALL_PREFIX}/share ${CPACK_PACKAGING_INSTALL_PREFIX}/share/doc ${CPACK_PACKAGING_INSTALL_PREFIX}/share/man ${CPACK_PACKAGING_INSTALL_PREFIX}/share/man/man3 ${CPACK_PACKAGING_INSTALL_PREFIX}/share/man/man7) set(CPACK_PACKAGE_NAME "libpmemkv") set(CPACK_PACKAGE_VERSION ${VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Key/Value Datastore for Persistent Memory") set(CPACK_PACKAGE_VENDOR "Intel") set(CPACK_RPM_PACKAGE_NAME "libpmemkv-devel") set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") set(CPACK_RPM_PACKAGE_LICENSE "BSD") set(CPACK_RPM_PACKAGE_ARCHITECTURE x86_64) set(CPACK_RPM_PACKAGE_AUTOREQ "no") set(CPACK_RPM_PACKAGE_REQUIRES ${RPM_PACKAGE_REQUIRES}) #set(CPACK_RPM_CHANGELOG_FILE ${CMAKE_SOURCE_DIR}/ChangeLog) set(CPACK_DEBIAN_PACKAGE_NAME "libpmemkv-dev") set(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64) set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_PACKAGE_REQUIRES}) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "lukasz.stolarczuk@intel.com") if("${CPACK_GENERATOR}" STREQUAL "RPM") set(CPACK_PACKAGE_FILE_NAME ${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.${CPACK_RPM_PACKAGE_ARCHITECTURE}) elseif("${CPACK_GENERATOR}" STREQUAL "DEB") set(CPACK_PACKAGE_FILE_NAME ${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) endif() set(targetDestDir ${CMAKE_INSTALL_TMPDIR}) include(CPack) pmemkv-1.1/cmake/rapidjson.cmake000066400000000000000000000043051361504041500167260ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. if(PKG_CONFIG_FOUND) pkg_check_modules(RapidJSON RapidJSON) endif() if(NOT RapidJSON_FOUND) # try old method # find_package without unsetting this var is not working correctly unset(RapidJSON_FOUND CACHE) find_package(RapidJSON REQUIRED) if(RapidJSON_FOUND) set(RapidJSON_LIBRARIES ${RapidJSON_LIBRARY}) set(RapidJSON_INCLUDE_DIRS ${RapidJSON_INCLUDE_DIR}) message(STATUS "RapidJSON library found the old way (w/o pkg-config)") endif() endif() if(NOT RapidJSON_FOUND) message(FATAL_ERROR "RapidJSON library not found") endif() include_directories(${RapidJSON_INCLUDE_DIRS}) link_directories(${RapidJSON_LIBRARY_DIRS}) pmemkv-1.1/cmake/redis-experimental.cmake000066400000000000000000000035661361504041500205460ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # FIXME hardcoded paths is a VERY bad idea set(LIB_ACL $ENV{HOME}/work/libacl/lib_acl) set(LIB_ACL_CPP $ENV{HOME}/work/libacl/lib_acl_cpp) set(LIB_PROTOCOL $ENV{HOME}/work/libacl/lib_protocol/lib) include_directories(SYSTEM ${LIB_ACL}/include ${LIB_ACL_CPP}/include/acl_cpp) link_directories(${LIB_ACL_CPP}/lib ${LIB_PROTOCOL} ${LIB_ACL}/lib) pmemkv-1.1/cmake/tbb.cmake000066400000000000000000000040561361504041500155070ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. if(PKG_CONFIG_FOUND) pkg_check_modules(TBB tbb) endif() if(NOT TBB_FOUND) # find_package without unsetting this var is not working correctly unset(TBB_FOUND CACHE) find_package(TBB COMPONENTS tbb) if(TBB_FOUND) message(STATUS "TBB package found without pkg-config") endif() set(TBB_LIBRARIES ${TBB_IMPORTED_TARGETS}) endif() if(NOT TBB_FOUND) message(FATAL_ERROR "TBB not found. Please set TBB_DIR CMake variable if TBB \ is installed in a non-standard directory, like: -DTBB_DIR=") endif() pmemkv-1.1/codecov.yml000066400000000000000000000002521361504041500150150ustar00rootroot00000000000000coverage: ignore: - doc/ - examples/ - utils/ - src/valgrind/ - tests/ comment: layout: "diff, files" behavior: default require_changes: yes pmemkv-1.1/doc/000077500000000000000000000000001361504041500134165ustar00rootroot00000000000000pmemkv-1.1/doc/CMakeLists.txt000066400000000000000000000137221361504041500161630ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. add_check_whitespace(man ${CMAKE_CURRENT_SOURCE_DIR}/*.md) find_program(PANDOC NAMES pandoc) if(PANDOC) # Prepare C_EXAMPLE content to inject, based on the code # of the 'examples/pmemkv_basic_c/pmemkv_basic.c' example; # remove comments (with the license and the file description) # up to the first preprocessor's directive. file(READ ${CMAKE_SOURCE_DIR}/examples/pmemkv_basic_c/pmemkv_basic.c example_content) string(REGEX REPLACE "([/]+[*]+).*([*]+[/]+)([\n]+#)" "#" example_content "${example_content}") set(C_EXAMPLE "${example_content}") configure_file(${CMAKE_SOURCE_DIR}/doc/libpmemkv.3.md.in ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv.3.md) # Prepare C_EXAMPLE content to inject, based on the code # of the 'examples/pmemkv_config_c/pmemkv_config.c' example; # remove comments (with the license and the file description) # up to the first preprocessor's directive. file(READ ${CMAKE_SOURCE_DIR}/examples/pmemkv_config_c/pmemkv_config.c example_content) string(REGEX REPLACE "([/]+[*]+).*([*]+[/]+)([\n]+#)" "#" example_content "${example_content}") set(C_EXAMPLE "${example_content}") configure_file(${CMAKE_SOURCE_DIR}/doc/libpmemkv_config.3.md.in ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv_config.3.md) # convert md files to manpage format add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/man/libpmemkv.7 MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/doc/libpmemkv.7.md COMMAND ${CMAKE_SOURCE_DIR}/utils/md2man/md2man.sh ${CMAKE_SOURCE_DIR}/doc/libpmemkv.7.md ${CMAKE_SOURCE_DIR}/utils/md2man/default.man ${CMAKE_BINARY_DIR}/man/libpmemkv.7 ${VERSION}) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/man/libpmemkv.3 MAIN_DEPENDENCY ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv.3.md COMMAND ${CMAKE_SOURCE_DIR}/utils/md2man/md2man.sh ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv.3.md ${CMAKE_SOURCE_DIR}/utils/md2man/default.man ${CMAKE_BINARY_DIR}/man/libpmemkv.3 ${VERSION}) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/man/libpmemkv_config.3 MAIN_DEPENDENCY ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv_config.3.md COMMAND ${CMAKE_SOURCE_DIR}/utils/md2man/md2man.sh ${CMAKE_BINARY_DIR}/man/tmp/libpmemkv_config.3.md ${CMAKE_SOURCE_DIR}/utils/md2man/default.man ${CMAKE_BINARY_DIR}/man/libpmemkv_config.3 ${VERSION}) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/man/libpmemkv_json_config.3 MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/doc/libpmemkv_json_config.3.md COMMAND ${CMAKE_SOURCE_DIR}/utils/md2man/md2man.sh ${CMAKE_SOURCE_DIR}/doc/libpmemkv_json_config.3.md ${CMAKE_SOURCE_DIR}/utils/md2man/default.man ${CMAKE_BINARY_DIR}/man/libpmemkv_json_config.3 ${VERSION}) # install manpages install(FILES ${CMAKE_BINARY_DIR}/man/libpmemkv.7 DESTINATION ${CMAKE_INSTALL_MANDIR}/man7) install(FILES ${CMAKE_BINARY_DIR}/man/libpmemkv.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) install(FILES ${CMAKE_BINARY_DIR}/man/libpmemkv_config.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) install(FILES ${CMAKE_BINARY_DIR}/man/libpmemkv_json_config.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) else() message(WARNING "pandoc not found - man pages will not be generated") endif() include(FindDoxygen) if(DOXYGEN_FOUND AND DOXYGEN_DOT_FOUND) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv.Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/libpmemkv.Doxyfile" @ONLY) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cpp_html/ DESTINATION ${CMAKE_INSTALL_DOCDIR}) elseif(NOT DOXYGEN_FOUND) message(WARNING "Doxygen not found - Doxygen documentation will not be generated") else() message(WARNING "Dot tool not found - Doxygen documentation will not be generated") endif() if(PANDOC AND DOXYGEN_FOUND AND DOXYGEN_DOT_FOUND) add_custom_target(doc ALL ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/libpmemkv.Doxyfile" DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv.7 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv.3 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv_config.3 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv_json_config.3 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) elseif(PANDOC) add_custom_target(doc ALL DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv.7 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv.3 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv_config.3 DEPENDS ${CMAKE_BINARY_DIR}/man/libpmemkv_json_config.3 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) elseif(DOXYGEN_FOUND AND DOXYGEN_DOT_FOUND) add_custom_target(doc ALL ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/libpmemkv.Doxyfile" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() pmemkv-1.1/doc/libpmemkv.3.md.in000066400000000000000000000270701361504041500165020ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV, 3) collection: libpmemkv header: PMEMKV secondary_title: pmemkv ... [comment]: <> (Copyright 2019-2020, Intel Corporation) [comment]: <> (Redistribution and use in source and binary forms, with or without) [comment]: <> (modification, are permitted provided that the following conditions) [comment]: <> (are met:) [comment]: <> ( * Redistributions of source code must retain the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer.) [comment]: <> ( * Redistributions in binary form must reproduce the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer in) [comment]: <> ( the documentation and/or other materials provided with the) [comment]: <> ( distribution.) [comment]: <> ( * Neither the name of the copyright holder nor the names of its) [comment]: <> ( contributors may be used to endorse or promote products derived) [comment]: <> ( from this software without specific prior written permission.) [comment]: <> (THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS) [comment]: <> ("AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR) [comment]: <> (A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT) [comment]: <> (OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,) [comment]: <> (SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,) [comment]: <> (DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY) [comment]: <> (THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT) [comment]: <> ((INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE) [comment]: <> (OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.) [comment]: <> (libpmemkv.3 -- man page for libpmemkv) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[EXAMPLE](#example)
[SEE ALSO](#see-also)
# NAME # **pmemkv** - Key/Value Datastore for Persistent Memory # SYNOPSIS # ```c #include typedef int pmemkv_get_kv_callback(const char *key, size_t keybytes, const char *value, size_t valuebytes, void *arg); typedef void pmemkv_get_v_callback(const char *value, size_t valuebytes, void *arg); int pmemkv_open(const char *engine, pmemkv_config *config, pmemkv_db **db); void pmemkv_close(pmemkv_db *kv); int pmemkv_count_all(pmemkv_db *db, size_t *cnt); int pmemkv_count_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, size_t *cnt); int pmemkv_get_all(pmemkv_db *db, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, pmemkv_get_kv_callback *c, void *arg); int pmemkv_exists(pmemkv_db *db, const char *k, size_t kb); int pmemkv_get(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_v_callback *c, void *arg); int pmemkv_get_copy(pmemkv_db *db, const char *k, size_t kb, char *buffer, size_t buffer_size, size_t *value_size); int pmemkv_put(pmemkv_db *db, const char *k, size_t kb, const char *v, size_t vb); int pmemkv_remove(pmemkv_db *db, const char *k, size_t kb); int pmemkv_defrag(pmemkv_db *db, double start_percent, double amount_percent); const char *pmemkv_errormsg(void); ``` For pmemkv configuration API description see **libpmemkv_config**(3). For general pmemkv information, engine descriptions and bindings details see **libpmemkv**(7). # DESCRIPTION # Keys and values stored in a pmemkv database can be arbitrary binary data and can contain multiple null characters. Every function which accepts key expects `const char *k` pointer to data and its size as `size_t`. Some of the functions (mainly range-query API) are not guaranteed to be implemented by all engines. If an engine does not support a certain function, it will return PMEMKV\_STATUS\_NOT\_SUPPORTED. `int pmemkv_open(const char *engine, pmemkv_config *config, pmemkv_db **db);` : Opens the pmemkv database and stores a pointer to a *pmemkv_db* instance in `*db`. The `engine` parameter specifies the engine name (see **libpmemkv**(7) for the list of available engines). The `config` parameter specifies configuration (see **libpmemkv_config**(3) for details). Pmemkv takes ownership of the config parameter - this means that pmemkv_config_delete() must NOT be called after successful open. `void pmemkv_close(pmemkv_db *kv);` : Closes pmemkv database. `int pmemkv_count_all(pmemkv_db *db, size_t *cnt);` : Stores in `*cnt` the number of records in `db`. `int pmemkv_count_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt);` : Stores in `*cnt` the number of records in `db` whose keys are greater than the key `k` of length `kb`. `int pmemkv_count_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt);` : Stores in `*cnt` the number of records in `db` whose keys are less than the key `k` of length `kb`. `int pmemkv_count_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, size_t *cnt);` : Stores in `*cnt` the number of records in `db` whose keys are greater than key `k1` (of length `kb1`) and less than key `k2` (of length `kb2`). `int pmemkv_get_all(pmemkv_db *db, pmemkv_get_kv_callback *c, void *arg);` : Executes function `c` for every record stored in `db`. Arguments passed to the function are: pointer to a key, size of the key, pointer to a value, size of the value and `arg` specified by the user. Function `c` can stop iteration by returning non-zero value. In that case *pmemkv_get_all()* returns PMEMKV\_STATUS\_STOPPED\_BY\_CB. Returning 0 continues iteration. `int pmemkv_get_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg);` : Executes function `c` for every record stored in `db` whose keys are greater than key `k` (of length `kb`). Arguments passed to `c` are: pointer to a key, size of the key, pointer to a value, size of the value and `arg` specified by the user. Function `c` can stop iteration by returning non-zero value. In that case *pmemkv_get_above()* returns PMEMKV\_STATUS\_STOPPED\_BY\_CB. Returning 0 continues iteration. `int pmemkv_get_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, oid *arg);` : Executes function `c` for every record stored in `db` whose keys are less than key `k` (of length `kb`). Arguments passed to `c` are: pointer to a key, size of the key, pointer to a value, size of the value and `arg` specified by the user. Function `c` can stop iteration by returning non-zero value. In that case *pmemkv_get_below()* returns PMEMKV\_STATUS\_STOPPED\_BY\_CB. Returning 0 continues iteration. `int pmemkv_get_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, pmemkv_get_kv_callback *c, void *arg);` : Executes function `c` for every record stored in `db` whose keys are greater than key `k1` (of length `kb1`) and less than key `k2` (of length `kb2`). Arguments passed to `c` are: pointer to a key, size of the key, pointer to a value, size of the value and `arg` specified by the user. Function `c` can stop iteration by returning non-zero value. In that case *pmemkv_get_between()* returns PMEMKV\_STATUS\_STOPPED\_BY\_CB. Returning 0 continues iteration. `int pmemkv_exists(pmemkv_db *db, const char *k, size_t kb);` : Checks existence of record with key `k` of length `kb`. If record is present PMEMKV\_STATUS\_OK is returned, otherwise PMEMKV\_STATUS\_NOT\_FOUND is returned. Other possible return values are described in the *ERRORS* section. `int pmemkv_get(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_v_callback *c, void *arg);` : Executes function `c` on record with key `k` (of length `kb`). If record is present and no error occurred the function returns PMEMKV\_STATUS\_OK. If record does not exist PMEMKV\_STATUS\_NOT\_FOUND is returned. Other possible return values are described in the *ERRORS* section. Function `c` is called with the following parameters: pointer to a value, size of the value and `arg` specified by the user. `Value` points to the location where data is actually stored (no copy occurs). This function is guaranteed to be implemented by all engines. `int pmemkv_get_copy(pmemkv_db *db, const char *k, size_t kb, char *buffer, size_t buffer_size, size_t *value_size);` : Copies value of record with key `k` of length `kb` to user provided buffer. `buffer` points to the value buffer, `buffer_size` specifies its size and `*value_size` is filled in by this function. If the value doesn't fit in the provided buffer then this function returns PMEMKV\_STATUS\_UNKNOWN\_ERROR. Otherwise, in absence of any errors, PMEMKV\_STATUS\_OK is returned. Other possible return values are described in the *ERRORS* section. This function is guaranteed to be implemented by all engines. `int pmemkv_put(pmemkv_db *db, const char *k, size_t kb, const char *v, size_t vb);` : Inserts a key-value pair into pmemkv database. `kb` is the length of key `k` and `vb` is the length of value `v`. When this function returns, caller is free to reuse both buffers. This function is guaranteed to be implemented by all engines. `int pmemkv_remove(pmemkv_db *db, const char *k, size_t kb);` : Removes record with key `k` of length `kb`. This function is guaranteed to be implemented by all engines. `int pmemkv_defrag(pmemkv_db *db, double start_percent, double amount_percent);` : Defragments approximately 'amount_percent' percent of elements in the database starting from 'start_percent' percent of elements. `const char *pmemkv_errormsg(void);` : Returns a human readable string describing the last error. ## ERRORS ## Each function, except for *pmemkv_close()* and *pmemkv_errormsg()*, returns one of the following status codes: + **PMEMKV_STATUS_OK** -- no error + **PMEMKV_STATUS_UNKNOWN_ERROR** -- unknown error + **PMEMKV_STATUS_NOT_FOUND** -- record not found + **PMEMKV_STATUS_NOT_SUPPORTED** -- function is not implemented by current engine + **PMEMKV_STATUS_INVALID_ARGUMENT** -- argument to function has wrong value + **PMEMKV_STATUS_CONFIG_PARSING_ERROR** -- parsing data to config failed + **PMEMKV_STATUS_CONFIG_TYPE_ERROR** -- config item has different type than expected + **PMEMKV_STATUS_STOPPED_BY_CB** -- iteration was stopped by user's callback + **PMEMKV_STATUS_OUT_OF_MEMORY** -- operation failed because there is not enough memory (or space on the device) + **PMEMKV_STATUS_WRONG_ENGINE_NAME** -- engine name does not match any available engine + **PMEMKV_STATUS_TRANSACTION_SCOPE_ERROR** -- an error with the scope of the libpmemobj transaction + **PMEMKV_STATUS_DEFRAG_ERROR** -- the defragmentation process failed (possibly in the middle of a run) Status returned from a function can change in a future version of a library to a more specific one. For example, if a function returns PMEMKV_STATUS_UNKNOWN_ERROR, it is possible that in future versions it will return PMEMKV_STATUS_INVALID_ARGUMENT. Recommended way to check for an error is to compare status with PMEMKV_STATUS_OK. # EXAMPLE # ```c @C_EXAMPLE@ ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv_config**(3) and **** pmemkv-1.1/doc/libpmemkv.7.md000066400000000000000000000211321361504041500160720ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV, 7) collection: libpmemkv header: PMEMKV secondary_title: pmemkv ... [comment]: <> (Copyright 2019, Intel Corporation) [comment]: <> (Redistribution and use in source and binary forms, with or without) [comment]: <> (modification, are permitted provided that the following conditions) [comment]: <> (are met:) [comment]: <> ( * Redistributions of source code must retain the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer.) [comment]: <> ( * Redistributions in binary form must reproduce the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer in) [comment]: <> ( the documentation and/or other materials provided with the) [comment]: <> ( distribution.) [comment]: <> ( * Neither the name of the copyright holder nor the names of its) [comment]: <> ( contributors may be used to endorse or promote products derived) [comment]: <> ( from this software without specific prior written permission.) [comment]: <> (THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS) [comment]: <> ("AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR) [comment]: <> (A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT) [comment]: <> (OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,) [comment]: <> (SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,) [comment]: <> (DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY) [comment]: <> (THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT) [comment]: <> ((INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE) [comment]: <> (OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.) [comment]: <> (libpmemkv.7 -- man page for libpmemkv) [NAME](#name)
[DESCRIPTION](#description)
[ENGINES](#engines)
[BINDINGS](#bindings)
[SEE ALSO](#see-also)
# NAME # **pmemkv** - Key/Value Datastore for Persistent Memory # DESCRIPTION # **pmemkv** is a key-value datastore framework optimized for persistent memory. It provides native C API and C++ headers. Support for other languages is described in the **BINDINGS** section below. It has multiple storage engines, each optimized for a different use case. They differ in implementation and capabilities: + persistence - this is a trade-off between data preservation and performance; persistent engines retain their content and are power fail/crash safe, but are slower; volatile engines are faster, but keep their content only until the database is closed (or application crashes; power fail occurs) + concurrency - engines provide a varying degree of write scalability in multi-threaded workloads. Concurrent engines support non-blocking retrievals and, on average, highly scalable updates. For details see the description of individual engines. + keys' ordering - "sorted" engines support querying above/below the given key Persistent engines usually use libpmemobj++ and PMDK to access NVDIMMs. They can work with files on DAX filesystem (fsdax) or DAX device. For description of pmemkv core API see **libpmemkv**(3). For description of pmemkv configuration API see **libpmemkv_config**(3). # ENGINES # | Engine Name | Description | Persistent? | Concurrent? | Sorted? | | ------------ | ----------- | ----------- | ----------- | ------- | | **cmap** | **Concurrent hash map** | **Yes** | **Yes** | **No** | | vcmap | Volatile concurrent hash map | No | Yes | No | | vsmap | Volatile sorted hash map | No | No | Yes | | blackhole | Accepts everything, returns nothing | No | Yes | No | The most mature and recommended engine to use for persistent use-cases is **cmap**. It provides good performance results and stability. Each engine can be manually turned on and off at build time, using CMake options. All engines listed here are enabled and ready to use. ## cmap A persistent concurrent engine, backed by a hashmap that allows calling get, put, and remove concurrently from multiple threads and ensures good scalability. Rest of the methods (e.g. range query methods) are not thread-safe and should not be called from more than one thread. Data stored using this engine is persistent and guaranteed to be consistent in case of any kind of interruption (crash / power loss / etc). Internally this engine uses persistent concurrent hashmap and persistent string from libpmemobj-cpp library (for details see ). Persistent string is used as a type of a key and a value. Engine's functions should not be called within libpmemobj transactions (improper call by user will result thrown exception). libpmemobj-cpp packages are required. This engine requires the following config parameters (see **libpmemkv_config**(3) for details how to set them): * **path** -- Path to a database file or to a poolset file (see **poolset**(5) for details). Not that when using poolset file, size should be 0 + type: string * **force_create** -- If 0, pmemkv opens file specified by 'path', otherwise it creates it. + type: uint64_t + default value: 0 * **size** -- Only needed when force_create is not 0, specifies size of the database [in bytes]. + type: uint64_t + min value: 8388608 (8MB) * **oid** -- Pointer to oid (for details see **libpmemobj**(7)) which points to engine data. If oid is null, engine will allocate new data, otherwise it will use existing one. + type: object The following table shows three possible combinations of parameters (where '-' means 'cannot be set'): | **#** | **path** | **force_create** | **size** | **oid** | | ----- | -------- | ---------------- | -------- | ------- | | **1** | set | 0 | - | - | | **2** | set | 1 | set | - | | **3** | - | - | - | set | A database file or a poolset file can also be created using **pmempool** utility (see **pmempool-create**(1)). When using **pmempool create**, "pmemkv" should be passed as layout. Only PMEMOBJ pools are supported. ## vcmap A volatile concurrent engine, backed by memkind. Data written using this engine is lost after database is closed. This engine is built on top of tbb::concurrent\_hash\_map data structure and uses PMEM C++ allocator to allocate memory. std::basic\_string is used as a type of a key and a value. Memkind, TBB and libpmemobj-cpp packages are required. This engine requires the following config parameters (see **libpmemkv_config**(3) for details how to set them): * **path** -- Path to an existing directory + type: string * **size** -- Specifies size of the database [in bytes] + type: uint64_t + min value: 8388608 (8MB) ## vsmap A volatile single-threaded sorted engine, backed by memkind. Data written using this engine is lost after database is closed. This engine is built on top of std::map and uses PMEM C++ allocator to allocate memory. std::basic\_string is used as a type of a key and a value. Memkind and libpmemobj-cpp packages are required. This engine requires the following config parameters (see **libpmemkv_config**(3) for details how to set them): * **path** -- Path to an existing directory + type: string * **size** -- Specifies size of the database [in bytes] + type: uint64_t + min value: 8388608 (8MB) ## blackhole A volatile engine that accepts an unlimited amount of data, but never returns anything. Internally, `blackhole` does not use a persistent pool or any durable structure. The intended use of this engine is to profile and tune high-level bindings, and similar cases when persistence should be intentionally skipped. No additional packages are required. No supported configuration parameters. ### Experimental engines There are also more engines in various states of development, for details see . Two of them (tree3 and stree) requires the config parameters like cmap and similarly to cmap should not be used within libpmemobj transaction(s). # BINDINGS # Bindings for other languages are available on GitHub. Currently they support only subset of native API. Existing bindings: + Java - for details see + JNI - for details see + Node.js - for details see + Python - for details see + Ruby - for details see # SEE ALSO # **libpmemkv**(3), **libpmemkv_config(3)**, **pmempool**(1), **libpmemobj**(7) and **** pmemkv-1.1/doc/libpmemkv.Doxyfile.in000066400000000000000000000225421361504041500175230ustar00rootroot00000000000000#--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See https://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "PMEMKV" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "This is the C++ documentation for PMEMKV." # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES EXTRACT_ALL = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. QUIET = YES #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_SOURCE_DIR@/src INPUT += @CMAKE_SOURCE_DIR@/src/README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = libpmemkv.hpp # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to strip. # Note that you can specify absolute paths here, but also relative paths, which will # be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. FULL_PATH_NAMES = YES STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/include/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name # of the header file containing the class definition is used. Otherwise one # should specify the list of include paths that are normally passed to the # compiler using the -I flag. STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@/include/ #--------------------------------------------------------------------------- # Configuration options related to the LATEX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES doxygen will generate LATEX output # The default value is: YES. GENERATE_LATEX = NO #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = cpp_html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. # gcc). The argument of the tag is a list of macros of the form: name or # name=definition (no spaces). If the definition and the "=" are omitted, "=1" # is assumed. To prevent a macro definition from being undefined via #undef or # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = __cpp_lib_uncaught_exceptions _WIN32 pmemkv-1.1/doc/libpmemkv_config.3.md.in000066400000000000000000000171021361504041500200220ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_CONFIG, 3) collection: libpmemkv header: PMEMKV_CONFIG secondary_title: pmemkv ... [comment]: <> (Copyright 2019, Intel Corporation) [comment]: <> (Redistribution and use in source and binary forms, with or without) [comment]: <> (modification, are permitted provided that the following conditions) [comment]: <> (are met:) [comment]: <> ( * Redistributions of source code must retain the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer.) [comment]: <> ( * Redistributions in binary form must reproduce the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer in) [comment]: <> ( the documentation and/or other materials provided with the) [comment]: <> ( distribution.) [comment]: <> ( * Neither the name of the copyright holder nor the names of its) [comment]: <> ( contributors may be used to endorse or promote products derived) [comment]: <> ( from this software without specific prior written permission.) [comment]: <> (THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS) [comment]: <> ("AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR) [comment]: <> (A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT) [comment]: <> (OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,) [comment]: <> (SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,) [comment]: <> (DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY) [comment]: <> (THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT) [comment]: <> ((INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE) [comment]: <> (OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.) [comment]: <> (libpmemkv_config.3 -- man page for libpmemkv configuration API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[EXAMPLE](#example)
[SEE ALSO](#see-also)
# NAME # **pmemkv_config** - Configuration API for libpmemkv # SYNOPSIS # ```c #include pmemkv_config *pmemkv_config_new(void); void pmemkv_config_delete(pmemkv_config *config); int pmemkv_config_put_data(pmemkv_config *config, const char *key, const void *value, size_t value_size); int pmemkv_config_put_object(pmemkv_config *config, const char *key, void *value, void (*deleter)(void *)); int pmemkv_config_put_uint64(pmemkv_config *config, const char *key, uint64_t value); int pmemkv_config_put_int64(pmemkv_config *config, const char *key, int64_t value); int pmemkv_config_put_string(pmemkv_config *config, const char *key, const char *value); int pmemkv_config_get_data(pmemkv_config *config, const char *key, const void **value, size_t *value_size); int pmemkv_config_get_object(pmemkv_config *config, const char *key, void **value); int pmemkv_config_get_uint64(pmemkv_config *config, const char *key, uint64_t *value); int pmemkv_config_get_int64(pmemkv_config *config, const char *key, int64_t *value); int pmemkv_config_get_string(pmemkv_config *config, const char *key, const char **value); ``` For general description of pmemkv and available engines see **libpmemkv**(7). For description of pmemkv core API see **libpmemkv**(3). # DESCRIPTION # pmemkv database is configured using pmemkv_config structure. It stores mappings of keys (null-terminated strings) to values. A value can be: + **uint64_t** + **int64_t** + **c-style string** + **binary data** + **pointer to an object** (with accompanying deleter function) It also delivers methods to store and read configuration items provided by a user. Once the configuration object is set (with all required parameters), it can be passed to *pmemkv_open()* method. List of options which are required by pmemkv database is specific to an engine. Every engine has documented all supported config parameters (please see **libpmemkv**(7) for details). `pmemkv_config *pmemkv_config_new(void);` : Creates an instance of configuration for pmemkv database. On failure, NULL is returned. `void pmemkv_config_delete(pmemkv_config *config);` : Deletes pmemkv_config. Should be called ONLY for configs which were not passed to pmemkv_open (as this function moves ownership of the config to the database). `int pmemkv_config_put_uint64(pmemkv_config *config, const char *key, uint64_t value);` : Puts uint64_t value `value` to pmemkv_config at key `key`. `int pmemkv_config_put_int64(pmemkv_config *config, const char *key, int64_t value);` : Puts int64_t value `value` to pmemkv_config at key `key`. `int pmemkv_config_put_string(pmemkv_config *config, const char *key, const char *value);` : Puts null-terminated string to pmemkv_config. The string is copied to the config. `int pmemkv_config_put_data(pmemkv_config *config, const char *key, const void *value, size_t value_size);` : Puts copy of binary data pointed by `value` to pmemkv_config. `value_size` specifies size of the data. `int pmemkv_config_put_object(pmemkv_config *config, const char *key, void *value, void (*deleter)(void *));` : Puts `value` to pmemkv_config. `value` can point to arbitrary object. `deleter` parameter specifies function which will be called for `value` when the config is destroyed (using pmemkv_config_delete). `int pmemkv_config_get_uint64(pmemkv_config *config, const char *key, uint64_t *value);` : Gets value of a config item with key `key`. Value is copied to variable pointed by `value`. `int pmemkv_config_get_int64(pmemkv_config *config, const char *key, int64_t *value);` : Gets value of a config item with key `key`. Value is copied to variable pointed by `value`. `int pmemkv_config_get_string(pmemkv_config *config, const char *key, const char **value);` : Gets pointer to a null-terminated string. The string is not copied. After successful call `value` points to string stored in pmemkv_config. `int pmemkv_config_get_data(pmemkv_config *config, const char *key, const void **value, size_t *value_size);` : Gets pointer to binary data. Data is not copied. After successful call `*value` points to data stored in pmemkv_config and `value_size` holds size of the data. `int pmemkv_config_get_object(pmemkv_config *config, const char *key, const void **value);` : Gets pointer to an object. After successful call, `*value` points to the object. Config items stored in pmemkv_config, which were put using a specific function can be obtained only using corresponding pmemkv_config_get_ function (for example, config items put using pmemkv_config_put_object can only be obtained using pmemkv_config_get_object). Exception from this rule are functions for uint64 and int64. If value put by pmemkv_config_put_int64 is in uint64_t range it can be obtained using pmemkv_config_get_uint64 and vice versa. ## ERRORS ## Each function, except for *pmemkv_config_new()* and *pmemkv_config_delete()*, returns status. Possible return values are: + **PMEMKV_STATUS_OK** -- no error + **PMEMKV_STATUS_UNKNOWN_ERROR** -- unknown error + **PMEMKV_STATUS_NOT_FOUND** -- record (or config item) not found + **PMEMKV_STATUS_CONFIG_PARSING_ERROR** -- parsing data to config failed + **PMEMKV_STATUS_CONFIG_TYPE_ERROR** -- config item has different type than expected # EXAMPLE # The following example is taken from `examples/pmemkv_config_c/pmemkv_config.c`. ```c @C_EXAMPLE@ ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3) , **libpmemkv_json_config**(3) and **** pmemkv-1.1/doc/libpmemkv_json_config.3.md000066400000000000000000000100671361504041500204510ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_JSON_CONFIG, 3) collection: libpmemkv header: PMEMKV_JSON_CONFIG secondary_title: pmemkv_json_config ... [comment]: <> (Copyright 2019, Intel Corporation) [comment]: <> (Redistribution and use in source and binary forms, with or without) [comment]: <> (modification, are permitted provided that the following conditions) [comment]: <> (are met:) [comment]: <> ( * Redistributions of source code must retain the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer.) [comment]: <> ( * Redistributions in binary form must reproduce the above copyright) [comment]: <> ( notice, this list of conditions and the following disclaimer in) [comment]: <> ( the documentation and/or other materials provided with the) [comment]: <> ( distribution.) [comment]: <> ( * Neither the name of the copyright holder nor the names of its) [comment]: <> ( contributors may be used to endorse or promote products derived) [comment]: <> ( from this software without specific prior written permission.) [comment]: <> (THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS) [comment]: <> ("AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR) [comment]: <> (A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT) [comment]: <> (OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,) [comment]: <> (SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT) [comment]: <> (LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,) [comment]: <> (DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY) [comment]: <> (THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT) [comment]: <> ((INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE) [comment]: <> (OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.) [comment]: <> (libpmemkv_json_config.3 -- man page for libpmemkv_json_config configuration API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[EXAMPLE](#example)
[SEE ALSO](#see-also)
# NAME # **pmemkv_json_config** - helper configuration API for libpmemkv # SYNOPSIS # ```c #include int pmemkv_config_from_json(pmemkv_config *config, const char *jsonconfig); const char *pmemkv_config_from_json_errormsg(void); ``` For general description of pmemkv and available engines see **libpmemkv**(7). For description of pmemkv core API see **libpmemkv**(3). For description of configuration API for libpmemkv see **libpmemkv_config**(3). # DESCRIPTION # pmemkv_json_config is a helper library that provides two functions: `int pmemkv_config_from_json(pmemkv_config *config, const char *jsonconfig);` : Parses JSON string and puts all items found in JSON into `config`. Allowed types in JSON strings and their corresponding types in pmemkv_config are: + **number** -- int64 or uint64 + **string** -- const char * + **object** -- (another JSON string) -> pointer to pmemkv_config (can be obtained using pmemkv_config_get_object) + **True**, **False** -- int64 `const char *pmemkv_config_from_json_errormsg(void);` : Returns a human readable string describing the last error. The 'pmemkv_config_from_json' function depends on RapidJSON library what is the direct cause of the creation of this small library. The building of this library is enabled by default. It can be disabled by setting the **BUILD_JSON_CONFIG** CMake variable to OFF: ```sh cmake .. -DBUILD_JSON_CONFIG=OFF ``` ## ERRORS ## The *pmemkv_config_from_json()* function returns status. Possible return values are: + **PMEMKV_STATUS_OK** -- no error + **PMEMKV_STATUS_UNKNOWN_ERROR** -- unknown error + **PMEMKV_STATUS_CONFIG_PARSING_ERROR** -- parsing config data failed # EXAMPLE # An example can be found in **libpmemkv_config**(3). # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3), **libpmemkv_config**(3) and **** pmemkv-1.1/examples/000077500000000000000000000000001361504041500144675ustar00rootroot00000000000000pmemkv-1.1/examples/CMakeLists.txt000066400000000000000000000066341361504041500172400ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. add_common_flag(-Wno-unused-but-set-variable) add_cppstyle(examples-pmemkv_basic_c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_c/*.c) add_check_whitespace(examples-pmemkv_basic_c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_c/*.c) add_cppstyle(examples-pmemkv_basic_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_cpp/*.cpp) add_check_whitespace(examples-pmemkv_basic_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_cpp/*.cpp) add_cppstyle(examples-pmemkv_pmemobj_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_pmemobj_cpp/*.cpp) add_check_whitespace(examples-pmemkv_pmemobj_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_pmemobj_cpp/*.cpp) add_cppstyle(examples-pmemkv_config_c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_config_c/*.c) add_check_whitespace(examples-pmemkv_config_c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_config_c/*.c) add_cppstyle(examples-pmemkv_open_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_open_cpp/*.cpp) add_check_whitespace(examples-pmemkv_open_cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_open_cpp/*.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) function(add_example name) set(srcs ${ARGN}) prepend(srcs ${CMAKE_CURRENT_SOURCE_DIR} ${srcs}) add_executable(example-${name} ${srcs}) endfunction() add_example(pmemkv_basic_cpp pmemkv_basic_cpp/pmemkv_basic.cpp) target_link_libraries(example-pmemkv_basic_cpp pmemkv) add_example(pmemkv_basic_c pmemkv_basic_c/pmemkv_basic.c) target_link_libraries(example-pmemkv_basic_c pmemkv) if(ENGINE_CMAP) add_example(pmemkv_pmemobj_cpp pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp) target_link_libraries(example-pmemkv_pmemobj_cpp pmemkv ${LIBPMEMOBJ++_LIBRARIES}) endif() if(BUILD_JSON_CONFIG) add_example(pmemkv_config_c pmemkv_config_c/pmemkv_config.c) target_link_libraries(example-pmemkv_config_c pmemkv pmemkv_json_config) endif() if(ENGINE_CMAP) add_example(pmemkv_open_cpp pmemkv_open_cpp/pmemkv_open.cpp) target_link_libraries(example-pmemkv_open_cpp pmemkv) endif() pmemkv-1.1/examples/README000066400000000000000000000014301361504041500153450ustar00rootroot00000000000000This directory contains C and C++ examples for pmemkv, the library providing key-value datastore optimized for persistent memory. For more information see libpmemkv(3). pmemkv_basic_c/pmemkv_basic.c -- contains basic example workflow of C application pmemkv_basic_cpp/pmemkv_basic.cpp -- contains basic example workflow of C++ application pmemkv_config_c/pmemkv_config.c -- contains example usage of pmemkv config API. It requires to be built: - 'rapidjson-devel' package to be installed in the OS and - 'BUILD_JSON_CONFIG' CMake variable to be set to ON pmemkv_pmemobj_cpp/pmemkv_pmemobj_basic.cpp -- contains example usage of pmemkv supporting multiple engines pmemkv_open_cpp/pmemkv_open_cpp -- contains example usage of pmemkv for already existing pools (and poolsets) pmemkv-1.1/examples/example.poolset000066400000000000000000000000761361504041500175340ustar00rootroot00000000000000PMEMPOOLSET 100M /dev/shm/pool.part0 100M /dev/shm/pool.part1 pmemkv-1.1/examples/pmemkv_basic_c/000077500000000000000000000000001361504041500174315ustar00rootroot00000000000000pmemkv-1.1/examples/pmemkv_basic_c/CMakeLists.txt000066400000000000000000000036771361504041500222060ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.5) project(pmemkv_basic C) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) else() message(FATAL_ERROR "pkg-config not found") endif() include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_basic_c pmemkv_basic.c) target_link_libraries(pmemkv_basic_c ${LIBPMEMKV_LIBRARIES}) pmemkv-1.1/examples/pmemkv_basic_c/pmemkv_basic.c000066400000000000000000000074161361504041500222450ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * pmemkv_basic.c -- example usage of pmemkv. */ #include #include #include #include #include #define LOG(msg) puts(msg) #define MAX_VAL_LEN 64 static const uint64_t SIZE = 1024UL * 1024UL * 1024UL; int get_kv_callback(const char *k, size_t kb, const char *value, size_t value_bytes, void *arg) { printf(" visited: %s\n", k); return 0; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s file\n", argv[0]); exit(1); } /* See libpmemkv_config(3) for more detailed example of config creation */ LOG("Creating config"); pmemkv_config *cfg = pmemkv_config_new(); assert(cfg != NULL); int s = pmemkv_config_put_string(cfg, "path", argv[1]); assert(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_uint64(cfg, "size", SIZE); assert(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_uint64(cfg, "force_create", 1); assert(s == PMEMKV_STATUS_OK); LOG("Opening pmemkv database with 'cmap' engine"); pmemkv_db *db = NULL; s = pmemkv_open("cmap", cfg, &db); assert(s == PMEMKV_STATUS_OK); assert(db != NULL); LOG("Putting new key"); const char *key1 = "key1"; const char *value1 = "value1"; s = pmemkv_put(db, key1, strlen(key1), value1, strlen(value1)); assert(s == PMEMKV_STATUS_OK); size_t cnt; s = pmemkv_count_all(db, &cnt); assert(s == PMEMKV_STATUS_OK); assert(cnt == 1); LOG("Reading key back"); char val[MAX_VAL_LEN]; s = pmemkv_get_copy(db, key1, strlen(key1), val, MAX_VAL_LEN, NULL); assert(s == PMEMKV_STATUS_OK); assert(!strcmp(val, "value1")); LOG("Iterating existing keys"); const char *key2 = "key2"; const char *value2 = "value2"; const char *key3 = "key3"; const char *value3 = "value3"; pmemkv_put(db, key2, strlen(key2), value2, strlen(value2)); pmemkv_put(db, key3, strlen(key3), value3, strlen(value3)); pmemkv_get_all(db, &get_kv_callback, NULL); LOG("Removing existing key"); s = pmemkv_remove(db, key1, strlen(key1)); assert(s == PMEMKV_STATUS_OK); assert(pmemkv_exists(db, key1, strlen(key1)) == PMEMKV_STATUS_NOT_FOUND); LOG("Defragmenting the database"); s = pmemkv_defrag(db, 0, 100); assert(s == PMEMKV_STATUS_OK); LOG("Closing database"); pmemkv_close(db); return 0; } pmemkv-1.1/examples/pmemkv_basic_cpp/000077500000000000000000000000001361504041500177715ustar00rootroot00000000000000pmemkv-1.1/examples/pmemkv_basic_cpp/CMakeLists.txt000066400000000000000000000037071361504041500225400ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.5) project(pmemkv_basic CXX) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) else() message(FATAL_ERROR "pkg-config not found") endif() include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_basic_cpp pmemkv_basic.cpp) target_link_libraries(pmemkv_basic_cpp ${LIBPMEMKV_LIBRARIES}) pmemkv-1.1/examples/pmemkv_basic_cpp/pmemkv_basic.cpp000066400000000000000000000062161361504041500231420ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * pmemkv_basic.cpp -- example usage of pmemkv. */ #include #include #include #include #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; const uint64_t SIZE = 1024UL * 1024UL * 1024UL; int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " file\n"; exit(1); } /* See libpmemkv_config(3) for more detailed example of config creation */ LOG("Creating config"); config cfg; status s = cfg.put_string("path", argv[1]); assert(s == status::OK); s = cfg.put_uint64("size", SIZE); assert(s == status::OK); s = cfg.put_uint64("force_create", 1); assert(s == status::OK); LOG("Opening pmemkv database with 'cmap' engine"); db *kv = new db(); assert(kv != nullptr); s = kv->open("cmap", std::move(cfg)); assert(s == status::OK); LOG("Putting new key"); s = kv->put("key1", "value1"); assert(s == status::OK); size_t cnt; s = kv->count_all(cnt); assert(s == status::OK && cnt == 1); LOG("Reading key back"); std::string value; s = kv->get("key1", &value); assert(s == status::OK && value == "value1"); LOG("Iterating existing keys"); kv->put("key2", "value2"); kv->put("key3", "value3"); kv->get_all([](string_view k, string_view v) { LOG(" visited: " << k.data()); return 0; }); LOG("Defragmenting the database"); s = kv->defrag(0, 100); assert(s == status::OK); LOG("Removing existing key"); s = kv->remove("key1"); assert(s == status::OK); s = kv->exists("key1"); assert(s == status::NOT_FOUND); LOG("Closing database"); delete kv; return 0; } pmemkv-1.1/examples/pmemkv_config_c/000077500000000000000000000000001361504041500176155ustar00rootroot00000000000000pmemkv-1.1/examples/pmemkv_config_c/CMakeLists.txt000066400000000000000000000037311361504041500223610ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.5) project(pmemkv_config C) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv libpmemkv_json_config) else() message(FATAL_ERROR "pkg-config not found") endif() include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_config_c pmemkv_config.c) target_link_libraries(pmemkv_config_c ${LIBPMEMKV_LIBRARIES}) pmemkv-1.1/examples/pmemkv_config_c/pmemkv_config.c000066400000000000000000000076301361504041500226130ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * pmemkv_config.c -- example usage of pmemkv config API */ #include #include #include #include #include /* deleter for int pointer */ void free_int_ptr(void *ptr) { free(ptr); } int main() { pmemkv_config *config = pmemkv_config_new(); assert(config != NULL); /* Put int64_t value */ int status = pmemkv_config_put_int64(config, "size", 1073741824); assert(status == PMEMKV_STATUS_OK); char buffer[] = "ABC"; /* Put binary data stored in buffer */ status = pmemkv_config_put_data(config, "binary", buffer, 3); assert(status == PMEMKV_STATUS_OK); const void *data; size_t data_size; /* Get pointer to binary data stored in config */ status = pmemkv_config_get_data(config, "binary", &data, &data_size); assert(status == PMEMKV_STATUS_OK); assert(data_size == 3); assert(((const char *)data)[0] == 'A'); int *int_ptr = malloc(sizeof(int)); assert(int_ptr != NULL); *int_ptr = 10; /* Put pointer to dynamically allocated object, free_int_ptr is called on * pmemkv_config_delete */ status = pmemkv_config_put_object(config, "int_ptr", int_ptr, &free_int_ptr); assert(status == PMEMKV_STATUS_OK); int *get_int_ptr; /* Get pointer to object stored in config */ status = pmemkv_config_get_object(config, "int_ptr", (void **)&get_int_ptr); assert(status == PMEMKV_STATUS_OK); assert(*get_int_ptr == 10); pmemkv_config_delete(config); pmemkv_config *config_from_json = pmemkv_config_new(); assert(config_from_json != NULL); /* Parse JSON and put all items found into config_from_json */ status = pmemkv_config_from_json(config_from_json, "{\"path\":\"/dev/shm\",\ \"size\":1073741824,\ \"subconfig\":{\ \"size\":1073741824\ }\ }"); assert(status == PMEMKV_STATUS_OK); const char *path; status = pmemkv_config_get_string(config_from_json, "path", &path); assert(status == PMEMKV_STATUS_OK); assert(strcmp(path, "/dev/shm") == 0); pmemkv_config *subconfig; /* Get pointer to nested configuration "subconfig" */ status = pmemkv_config_get_object(config_from_json, "subconfig", (void **)&subconfig); assert(status == PMEMKV_STATUS_OK); size_t sub_size; status = pmemkv_config_get_uint64(subconfig, "size", &sub_size); assert(status == PMEMKV_STATUS_OK); assert(sub_size == 1073741824); pmemkv_config_delete(config_from_json); return 0; } pmemkv-1.1/examples/pmemkv_open_cpp/000077500000000000000000000000001361504041500176515ustar00rootroot00000000000000pmemkv-1.1/examples/pmemkv_open_cpp/CMakeLists.txt000066400000000000000000000037071361504041500224200ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.5) project(pmemkv_open_cpp CXX) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) else() message(FATAL_ERROR "pkg-config not found") endif() include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_open_cpp pmemkv_open.cpp) target_link_libraries(pmemkv_open_cpp ${LIBPMEMKV_LIBRARIES}) pmemkv-1.1/examples/pmemkv_open_cpp/pmemkv_open.cpp000066400000000000000000000063041361504041500227000ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * pmemkv_open.cpp -- example usage of pmemkv with already existing pools. */ #include #include #include #include #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; /** * This example expects a path to already created pool. * * To create a pool use one of the following commands. * * For regular pools use: * pmempool create -l -s 1G "pmemkv" obj path_to_a_pool * * For poolsets use: * pmempool create -l "pmemkv" obj ../examples/example.poolset */ int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " pool\n"; exit(1); } /* See libpmemkv_config(3) for more detailed example of creating a config */ LOG("Creating config"); config cfg; status s = cfg.put_string("path", argv[1]); assert(s == status::OK); LOG("Opening pmemkv database with 'cmap' engine"); db *kv = new db(); assert(kv != nullptr); s = kv->open("cmap", std::move(cfg)); assert(s == status::OK); LOG("Putting new key"); s = kv->put("key1", "value1"); assert(s == status::OK); size_t cnt; s = kv->count_all(cnt); assert(s == status::OK && cnt == 1); LOG("Reading key back"); std::string value; s = kv->get("key1", &value); assert(s == status::OK && value == "value1"); LOG("Iterating existing keys"); kv->put("key2", "value2"); kv->put("key3", "value3"); kv->get_all([](string_view k, string_view v) { LOG(" visited: " << k.data()); return 0; }); LOG("Removing existing key"); s = kv->remove("key1"); assert(s == status::OK); s = kv->exists("key1"); assert(s == status::NOT_FOUND); LOG("Closing database"); delete kv; return 0; } pmemkv-1.1/examples/pmemkv_pmemobj_cpp/000077500000000000000000000000001361504041500203415ustar00rootroot00000000000000pmemkv-1.1/examples/pmemkv_pmemobj_cpp/CMakeLists.txt000066400000000000000000000041631361504041500231050ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. cmake_minimum_required(VERSION 3.5) project(pmemkv_pmemobj CXX) set(LIBPMEMOBJ_CPP_REQUIRED_VERSION 1.9) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++>=${LIBPMEMOBJ_CPP_REQUIRED_VERSION}) else() message(FATAL_ERROR "pkg-config not found") endif() include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_pmemobj_cpp pmemkv_pmemobj.cpp) target_link_libraries(pmemkv_pmemobj_cpp ${LIBPMEMKV_LIBRARIES} ${LIBPMEMOBJ++_LIBRARIES}) pmemkv-1.1/examples/pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp000066400000000000000000000104711361504041500240600ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * pmemkv_pmemobj_basic.cpp -- example usage of pmemkv supporting multiple * engines. */ #include "../../src/pmemobj_engine.h" #include #include #include #include #include #include #include #include #include #undef LOG #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; using pmemoid_vector = pmem::obj::vector; const uint64_t SIZE = 1024UL * 1024UL * 1024UL; struct Root { pmem::obj::persistent_ptr oids; pmem::obj::persistent_ptr str; }; int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " file\n"; exit(1); } const char *path = argv[1]; pmem::obj::pool pop; try { pop = pmem::obj::pool::create(path, "pmemkv", SIZE, S_IRWXU); pmem::obj::transaction::run(pop, [&] { pop.root()->oids = pmem::obj::make_persistent(); pop.root()->str = pmem::obj::make_persistent(); pop.root()->oids->emplace_back(OID_NULL); pop.root()->oids->emplace_back(OID_NULL); }); LOG("Creating configs"); config cfg_1; config cfg_2; status ret = cfg_1.put_object("oid", &(pop.root()->oids->at(0)), nullptr); assert(ret == status::OK); ret = cfg_2.put_object("oid", &(pop.root()->oids->at(1)), nullptr); assert(ret == status::OK); LOG("Starting first cmap engine"); db *kv_1 = new db(); assert(kv_1 != nullptr); status s = kv_1->open("cmap", std::move(cfg_1)); assert(s == status::OK); *(pop.root()->str) = "some string"; LOG("Starting second cmap engine"); db *kv_2 = new db(); assert(kv_2 != nullptr); s = kv_2->open("cmap", std::move(cfg_2)); assert(s == status::OK); LOG("Putting new key into first cmap"); s = kv_1->put("key_1", "value_1"); assert(s == status::OK); LOG("Putting new key into second cmap"); s = kv_2->put("key_2", "value_2"); assert(s == status::OK); LOG("Reading key back from first cmap"); std::string value; s = kv_1->get("key_1", &value); assert(s == status::OK && value == "value_1"); LOG("Reading key back from second cmap"); value.clear(); s = kv_2->get("key_2", &value); assert(s == status::OK && value == "value_2"); LOG("Defragmenting the first cmap"); s = kv_1->defrag(0, 100); assert(s == status::OK); LOG("Defragmenting the second cmap"); s = kv_2->defrag(0, 100); assert(s == status::OK); LOG("Stopping first cmap engine"); delete kv_1; LOG("Stopping second cmap engine"); delete kv_2; } catch (std::exception &e) { std::cerr << e.what() << std::endl; } pop.close(); return 0; } pmemkv-1.1/libpmemkv.pc.in000066400000000000000000000006111361504041500155660ustar00rootroot00000000000000version=@VERSION@ prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ Name: libpmemkv Description: libpmemkv - Key/Value Datastore for Persistent Memory Version: ${version} URL: https://github.com/pmem/pmemkv Requires.private: @PKG_CONFIG_REQUIRES@ Libs: -L${libdir} -lpmemkv Cflags: -I${includedir} pmemkv-1.1/libpmemkv_json_config.pc.in000066400000000000000000000006451361504041500201530ustar00rootroot00000000000000version=@VERSION@ prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ Name: libpmemkv_json_config Description: libpmemkv_json_config - helper library for pmemkv Version: ${version} URL: https://github.com/pmem/pmemkv Requires: libpmemkv Requires.private: RapidJSON Libs: -L${libdir} -lpmemkv_json_config Cflags: -I${includedir} pmemkv-1.1/src/000077500000000000000000000000001361504041500134405ustar00rootroot00000000000000pmemkv-1.1/src/README.md000066400000000000000000000011211361504041500147120ustar00rootroot00000000000000pmemkv {#mainpage} =========================== Key/Value Datastore for Persistent Memory All general information about (lib)pmemkv can be found on webstie: https://pmem.io/pmemkv Here user can found description of C++ API. ### Important classes/functions ### * class pmem::kv::db, with its functions: * pmem::kv::db::open() to create/open file with data * pmem::kv::db::put() to insert data into database * pmem::kv::db::get() to read data back * pmem::kv::db::remove() to get rid of selected key (ant its value) * class pmem::kv::config to setup parameters to open/create database pmemkv-1.1/src/config.h000066400000000000000000000166601361504041500150670ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_CONFIG_H #define LIBPMEMKV_CONFIG_H #include #include #include #include #include #include #include "exceptions.h" #include "libpmemkv.hpp" #include "out.h" namespace pmem { namespace kv { namespace internal { struct config { public: config() = default; void put_data(const char *key, const void *value, size_t value_size) { put(key, value, value_size); } void put_object(const char *key, void *value, void (*deleter)(void *)) { put(key, value, deleter); } void put_int64(const char *key, int64_t value) { put(key, value); } void put_uint64(const char *key, uint64_t value) { put(key, value); } void put_string(const char *key, const char *value) { put(key, value); } bool get_data(const char *key, const void **value, size_t *value_size) { auto item = get(key); if (item == nullptr) return false; if (item->item_type != type::DATA) throw_type_error(key, item->item_type); *value = item->data.data(); *value_size = item->data.size(); return true; } /* * @return 'false' if no item with specified key exists, * 'true' if item was obtained successfully * * @throw pmem::kv::internal::type_error if item has type different than 'object' */ bool get_object(const char *key, void **value) { auto item = get(key); if (item == nullptr) return false; if (item->item_type != type::OBJECT) throw_type_error(key, item->item_type); *value = item->object.ptr; return true; } /* * @return 'false' if no item with specified key exists, * 'true' if item was obtained successfully * * @throw pmem::kv::internal::type_error if item has type different than 'int64' * or 'uint64' (and convertible to int64) */ bool get_int64(const char *key, int64_t *value) { auto item = get(key); if (item == nullptr) return false; if (item->item_type == type::INT64) *value = item->sint64; else if (item->item_type == type::UINT64) { /* Conversion from uint64 allowed */ if (item->uint64 <= static_cast(std::numeric_limits::max())) *value = static_cast(item->uint64); else throw config_type_error( "Item with key: " + std::string(key) + " has value which exceeds int64 range"); } else throw_type_error(key, item->item_type); return true; } /* * @return 'false' if no item with specified key exists, * 'true' if item was obtained successfully * * @throw pmem::kv::internal::type_error if item has type different than 'uint64' * or 'int64' (and convertible to uint64) */ bool get_uint64(const char *key, uint64_t *value) { auto item = get(key); if (item == nullptr) return false; if (item->item_type == type::UINT64) *value = item->uint64; else if (item->item_type == type::INT64) { /* Conversion from int64 allowed */ if (item->sint64 >= 0) *value = static_cast(item->sint64); else throw config_type_error( "Item with key: " + std::string(key) + " is < 0"); } else throw_type_error(key, item->item_type); return true; } /* * @return 'false' if no item with specified key exists, * 'true' if item was obtained successfully * * @throw pmem::kv::internal::type_error if item has type different than 'string' */ bool get_string(const char *key, const char **value) { auto item = get(key); if (item == nullptr) return false; if (item->item_type != type::STRING) throw_type_error(key, item->item_type); *value = item->string_v.c_str(); return true; } /* * Removes item from config, does not call its destructor in case it's an object. * * @throw pmem::kv::internal::error if item does not exist */ void remove(const char *key) { auto item = umap.find(key); if (item == umap.end()) throw error("Item with key: " + std::string(key) + " does not exist."); /* Prevent from calling deleter on item removal */ if (item->second.item_type == type::OBJECT) item->second.object.deleter = nullptr; umap.erase(item); } private: std::string type_names[6] = {"string", "int64", "uint64", "data", "object"}; enum class type { STRING, INT64, UINT64, DATA, OBJECT }; struct variant { variant(int64_t sint64) : sint64(sint64), item_type(type::INT64) { } variant(uint64_t uint64) : uint64(uint64), item_type(type::UINT64) { } variant(const char *string_v) : string_v(string_v), item_type(type::STRING) { } variant(void *object, void (*deleter)(void *)) : object{object, deleter}, item_type(type::OBJECT) { } variant(const void *data, std::size_t size) : data((const char *)data, (const char *)data + size), item_type(type::DATA) { } ~variant() { if (item_type == type::STRING) string_v.~basic_string(); else if (item_type == type::DATA) data.~vector(); else if (item_type == type::OBJECT && object.deleter != nullptr) object.deleter(object.ptr); } union { int64_t sint64; uint64_t uint64; std::string string_v; std::vector data; struct { void *ptr; void (*deleter)(void *); } object; }; type item_type; }; std::unordered_map umap; template void put(const std::string &key, Args &&... args) { auto ret = umap.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(std::forward(args)...)); if (!ret.second) throw error("Item with key: " + std::string(key) + " already exists"); } variant *get(const std::string &key) { auto found = umap.find(key); if (found == umap.end()) return nullptr; return &found->second; } void throw_type_error(const std::string &key, type item_type) { throw config_type_error("Item with key: " + std::string(key) + " is " + type_names[static_cast(item_type)]); } }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif pmemkv-1.1/src/engine.cc000066400000000000000000000132641361504041500152220ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "engine.h" #include "engines/blackhole.h" #ifdef ENGINE_VSMAP #include "engines/vsmap.h" #endif #ifdef ENGINE_VCMAP #include "engines/vcmap.h" #endif #ifdef ENGINE_CMAP #include "engines/cmap.h" #endif #ifdef ENGINE_CACHING #include "engines-experimental/caching.h" #endif #ifdef ENGINE_STREE #include "engines-experimental/stree.h" #endif #ifdef ENGINE_TREE3 #include "engines-experimental/tree3.h" #endif namespace pmem { namespace kv { engine_base::engine_base() { } engine_base::~engine_base() { } static constexpr const char *available_engines = "blackhole" #ifdef ENGINE_CMAP ", cmap" #endif #ifdef ENGINE_VSMAP ", vsmap" #endif #ifdef ENGINE_VCMAP ", vcmap" #endif #ifdef ENGINE_TREE3 ", tree3" #endif #ifdef ENGINE_STREE ", stree" #endif #ifdef ENGINE_CACHING ", caching" #endif ; /* throws internal error (status) if config is null */ void engine_base::check_config_null(const std::string &engine_name, std::unique_ptr &cfg) { if (!cfg) { throw internal::invalid_argument("Config cannot be null for the '" + engine_name + "' engine"); } } std::unique_ptr engine_base::create_engine(const std::string &engine, std::unique_ptr cfg) { if (engine == "blackhole") return std::unique_ptr( new pmem::kv::blackhole(std::move(cfg))); #ifdef ENGINE_CMAP if (engine == "cmap") { engine_base::check_config_null(engine, cfg); return std::unique_ptr(new pmem::kv::cmap(std::move(cfg))); } #endif #ifdef ENGINE_VSMAP if (engine == "vsmap") { engine_base::check_config_null(engine, cfg); return std::unique_ptr(new pmem::kv::vsmap(std::move(cfg))); } #endif #ifdef ENGINE_VCMAP if (engine == "vcmap") { engine_base::check_config_null(engine, cfg); return std::unique_ptr(new pmem::kv::vcmap(std::move(cfg))); } #endif #ifdef ENGINE_TREE3 if (engine == "tree3") { engine_base::check_config_null(engine, cfg); return std::unique_ptr(new pmem::kv::tree3(std::move(cfg))); } #endif #ifdef ENGINE_STREE if (engine == "stree") { engine_base::check_config_null(engine, cfg); return std::unique_ptr(new pmem::kv::stree(std::move(cfg))); } #endif #ifdef ENGINE_CACHING if (engine == "caching") { engine_base::check_config_null(engine, cfg); return std::unique_ptr( new pmem::kv::caching(std::move(cfg))); } #endif throw internal::wrong_engine_name("Unknown engine name \"" + engine + "\". Available engines: " + available_engines); } status engine_base::count_all(std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::count_above(string_view key, std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::count_equal_above(string_view key, std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::count_equal_below(string_view key, std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::count_below(string_view key, std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::count_between(string_view key1, string_view key2, std::size_t &cnt) { return status::NOT_SUPPORTED; } status engine_base::get_all(get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::get_above(string_view key, get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::get_below(string_view key, get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { return status::NOT_SUPPORTED; } status engine_base::exists(string_view key) { return status::NOT_SUPPORTED; } status engine_base::defrag(double start_percent, double amount_percent) { return status::NOT_SUPPORTED; } } // namespace kv } // namespace pmem pmemkv-1.1/src/engine.h000066400000000000000000000066551361504041500150720ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_ENGINE_H #define LIBPMEMKV_ENGINE_H #include #include #include #include "config.h" #include "libpmemkv.hpp" namespace pmem { namespace kv { const std::string LAYOUT = "pmemkv"; class engine_base { public: engine_base(); virtual ~engine_base(); static std::unique_ptr create_engine(const std::string &name, std::unique_ptr cfg); virtual std::string name() = 0; virtual status count_all(std::size_t &cnt); virtual status count_above(string_view key, std::size_t &cnt); virtual status count_equal_above(string_view key, std::size_t &cnt); virtual status count_equal_below(string_view key, std::size_t &cnt); virtual status count_below(string_view key, std::size_t &cnt); virtual status count_between(string_view key1, string_view key2, std::size_t &cnt); virtual status get_all(get_kv_callback *callback, void *arg); virtual status get_above(string_view key, get_kv_callback *callback, void *arg); virtual status get_equal_above(string_view key, get_kv_callback *callback, void *arg); virtual status get_equal_below(string_view key, get_kv_callback *callback, void *arg); virtual status get_below(string_view key, get_kv_callback *callback, void *arg); virtual status get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg); virtual status exists(string_view key); virtual status get(string_view key, get_v_callback *callback, void *arg) = 0; virtual status put(string_view key, string_view value) = 0; virtual status remove(string_view key) = 0; virtual status defrag(double start_percent, double amount_percent); private: static void check_config_null(const std::string &engine_name, std::unique_ptr &cfg); }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_ENGINE_H */ pmemkv-1.1/src/engines-experimental/000077500000000000000000000000001361504041500175635ustar00rootroot00000000000000pmemkv-1.1/src/engines-experimental/caching.cc000066400000000000000000000227451361504041500215000ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "caching.h" #include #include #include #include #include #include #include #define ZERO 0 namespace pmem { namespace kv { static time_t convertTimeToEpoch(const char *theTime, const char *format = "%Y%m%d%H%M%S") { std::tm tmTime; memset(&tmTime, 0, sizeof(tmTime)); strptime(theTime, format, &tmTime); return mktime(&tmTime); } static std::string getTimeStamp(const time_t epochTime, const char *format = "%Y%m%d%H%M%S") { char timestamp[64] = {0}; strftime(timestamp, sizeof(timestamp), format, localtime(&epochTime)); return timestamp; } static bool valueFieldConversion(std::string dateValue, int64_t ttl) { bool timeValidFlag = false; if (ttl > ZERO) { const time_t now = convertTimeToEpoch(dateValue.c_str()); struct tm then_tm = *localtime(&now); char ttlTimeStr[80]; then_tm.tm_sec += ttl; mktime(&then_tm); std::strftime(ttlTimeStr, 80, "%Y%m%d%H%M%S", &then_tm); const time_t curTime = time(0); std::string curTimeStr = getTimeStamp(curTime); if (ttlTimeStr >= curTimeStr) timeValidFlag = true; } return timeValidFlag; } caching::caching(std::unique_ptr cfg) { auto &config = *cfg; std::string subEngine; getString(config, "subengine", subEngine); getString(config, "remote_type", remoteType); getString(config, "remote_user", remoteUser); getString(config, "remote_pwd", remotePasswd); getString(config, "remote_url", remoteUrl); getString(config, "host", host); if (!config.get_int64("ttl", &ttl)) ttl = 0; if (!config.get_int64("port", &port)) throw internal::invalid_argument( "Config does not contain item with key: \"port\""); if (!config.get_int64("attempts", &attempts)) throw internal::invalid_argument( "Config does not contain item with key: \"attempts\""); internal::config *subEngineConfig; if (!config.get_object("subengine_config", (void **)&subEngineConfig)) throw internal::invalid_argument( "Config does not contain item with key: \"subengine_config\""); /* Remove item to pass ownership of it to subengine */ config.remove("subengine_config"); basePtr = engine_base::create_engine( subEngine, std::unique_ptr(subEngineConfig)); LOG("Started ok"); } caching::~caching() { LOG("Stopped ok"); } std::string caching::name() { return "caching"; } void caching::getString(internal::config &config, const char *key, std::string &str) { const char *value; if (!config.get_string(key, &value)) throw internal::invalid_argument( "Config does not contain item with key: \"" + std::string(key) + "\""); str = std::string(value); } status caching::count_all(std::size_t &cnt) { LOG("count_all"); std::size_t result = 0; get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { auto c = ((std::size_t *)arg); (*c)++; return 0; }, &result); cnt = result; return status::OK; } struct GetAllCacheCallbackContext { void *arg; get_kv_callback *cBack; std::list *expiredKeys; int64_t ttl; }; status caching::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); std::list removingKeys; GetAllCacheCallbackContext cxt = {arg, callback, &removingKeys, ttl}; auto cb = [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((GetAllCacheCallbackContext *)arg); std::string localValue = v; std::string timeStamp = localValue.substr(0, 14); std::string value = localValue.substr(14); // TTL from config is ZERO or if the key is valid if (!c->ttl || valueFieldConversion(timeStamp, c->ttl)) { auto ret = c->cBack(k, kb, value.c_str(), value.length(), c->arg); if (ret != 0) return ret; } else { c->expiredKeys->push_back(k); } return 0; }; if (basePtr) { // todo bail earlier if null auto s = basePtr->get_all(cb, &cxt); if (s != status::OK) return s; for (const auto &itr : removingKeys) { auto s = basePtr->remove(itr); if (s != status::OK) return s; } } return status::OK; } status caching::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); status s = status::NOT_FOUND; std::string value; if (getKey(std::string(key.data(), key.size()), value, true)) s = status::OK; return s; } status caching::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); std::string value; if (getKey(std::string(key.data(), key.size()), value, false)) { callback(value.c_str(), value.size(), arg); return status::OK; } else return status::NOT_FOUND; } bool caching::getKey(const std::string &key, std::string &valueField, bool api_flag) { auto cb = [](const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(v, vb); }; std::string value; basePtr->get(key, cb, &value); bool timeValidFlag; if (!value.empty()) { std::string timeStamp = value.substr(0, 14); valueField = value.substr(14); timeValidFlag = valueFieldConversion(timeStamp, ttl); } // No value for a key on local cache or if TTL not equal to zero and TTL is // expired if (value.empty() || (ttl && !timeValidFlag)) { // api_flag is true when request is from Exists API and no need to // connect to remote service api_flag is false when request is from Get // API and connect to remote service if (api_flag || (remoteType == "Redis" && !getFromRemoteRedis(key, valueField)) || (remoteType == "Memcached" && !getFromRemoteMemcached(key, valueField)) || (remoteType != "Redis" && remoteType != "Memcached")) return false; } put(key, valueField); return true; } bool caching::getFromRemoteMemcached(const std::string &key, std::string &value) { LOG("getFromRemoteMemcached"); bool retValue = false; memcached_server_st *servers = NULL; memcached_st *memc; memcached_return rc; memc = memcached_create(NULL); // todo reuse connection servers = memcached_server_list_append(servers, const_cast(host.c_str()), port, &rc); rc = memcached_server_push(memc, servers); // Multiple tries to connect to remote memcached for (int i = 0; i < attempts; ++i) { memcached_stat(memc, NULL, &rc); if (rc == MEMCACHED_SUCCESS) { size_t return_value_length; uint32_t flags; char *memcacheRet = memcached_get(memc, key.c_str(), key.length(), &return_value_length, &flags, &rc); if (memcacheRet) { value = memcacheRet; retValue = true; } break; } sleep(1); // todo expose as configurable attempt delay } return retValue; } bool caching::getFromRemoteRedis(const std::string &key, std::string &value) { LOG("getFromRemoteRedis"); bool retValue = false; std::string hostport = host + ":" + std::to_string(port); acl::string addr(hostport.c_str()), passwd; acl::acl_cpp_init(); // todo reuse connection acl::redis_client client(addr.c_str(), 0, 0); // Multiple tries to connect to remote redis for (int i = 0; i < attempts; ++i) { if (client.get_stream() && client.get_stream()->opened()) { client.set_password(passwd); acl::redis cmd(&client); acl::string key1, _value; key1.format("%s", key.c_str()); cmd.get(key1, _value); value = _value.c_str(); if (value.length() > 0) retValue = true; break; } sleep(1); // todo expose as configurable attempt delay } return retValue; } status caching::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); const time_t curSysTime = time(0); const std::string curTime = getTimeStamp(curSysTime); const std::string ValueWithCurTime = curTime + std::string(value.data(), value.size()); return basePtr->put(std::string(key.data(), key.size()), ValueWithCurTime); } status caching::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); return basePtr->remove(std::string(key.data(), key.size())); } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines-experimental/caching.h000066400000000000000000000052131361504041500213310ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../engine.h" namespace pmem { namespace kv { class db; class caching : public engine_base { public: caching(std::unique_ptr cfg); ~caching(); std::string name() final; status count_all(std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; private: void getString(internal::config &cfg, const char *key, std::string &str); bool getFromRemoteRedis(const std::string &key, std::string &value); bool getFromRemoteMemcached(const std::string &key, std::string &value); bool getKey(const std::string &key, std::string &valueField, bool api_flag); std::unique_ptr basePtr; int64_t attempts; std::string host; int64_t port; std::string remoteType; std::string remoteUser; std::string remotePasswd; std::string remoteUrl; int64_t ttl; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines-experimental/stree.cc000066400000000000000000000247101361504041500212200ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include #include #include "../out.h" #include "stree.h" using pmem::detail::conditional_add_to_tx; using pmem::obj::make_persistent_atomic; using pmem::obj::transaction; namespace pmem { namespace kv { stree::stree(std::unique_ptr cfg) : pmemobj_engine_base(cfg) { Recover(); LOG("Started ok"); } stree::~stree() { LOG("Stopped ok"); } std::string stree::name() { return "stree"; } status stree::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); auto result = std::distance(my_btree->begin(), my_btree->end()); assert(result >= 0); cnt = static_cast(result); return status::OK; } // above key, key exclusive status stree::count_above(string_view key, std::size_t &cnt) { LOG("count_above key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->upper_bound( pstring(key.data(), key.size())); auto result = std::distance(it, my_btree->end()); assert(result >= 0); cnt = static_cast(result); return status::OK; } // above or equal to key, key inclusive status stree::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_above key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->lower_bound( pstring(key.data(), key.size())); auto result = std::distance(it, my_btree->end()); assert(result >= 0); cnt = static_cast(result); return status::OK; } // below key, key exclusive status stree::count_below(string_view key, std::size_t &cnt) { LOG("count_below key<" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->lower_bound( pstring(key.data(), key.size())); auto result = std::distance(my_btree->begin(), it); assert(result >= 0); cnt = static_cast(result); return status::OK; } // below or equal to key, key inclusive status stree::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_above key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->upper_bound( pstring(key.data(), key.size())); auto result = std::distance(my_btree->begin(), it); assert(result >= 0); cnt = static_cast(result); return status::OK; } status stree::count_between(string_view key1, string_view key2, std::size_t &cnt) { LOG("count_between key range=[" << std::string(key1.data(), key1.size()) << "," << std::string(key2.data(), key2.size()) << ")"); check_outside_tx(); internal::stree::btree_type::iterator it1 = my_btree->upper_bound( pstring(key1.data(), key1.size())); internal::stree::btree_type::iterator it2 = my_btree->lower_bound( pstring(key2.data(), key2.size())); auto result = std::distance(it1, it2); assert(result >= 0); cnt = static_cast(result); return status::OK; } status stree::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); for (auto &iterator : *my_btree) { auto ret = callback(iterator.first.c_str(), iterator.first.size(), iterator.second.c_str(), iterator.second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } // (key, end), above key status stree::get_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_above start key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->upper_bound( pstring(key.data(), key.size())); while (it != my_btree->end()) { auto ret = callback((*it).first.c_str(), (*it).first.size(), (*it).second.c_str(), (*it).second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; it++; } return status::OK; } // [key, end), above or equal to key status stree::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above start key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->lower_bound( pstring(key.data(), key.size())); while (it != my_btree->end()) { auto ret = callback((*it).first.c_str(), (*it).first.size(), (*it).second.c_str(), (*it).second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; it++; } return status::OK; } // [start, key], below or equal to key status stree::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above start key>=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->begin(); auto pskey = pstring(key.data(), key.size()); while (it != my_btree->end() && !((*it).first > pskey)) { auto ret = callback((*it).first.c_str(), (*it).first.size(), (*it).second.c_str(), (*it).second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; it++; } return status::OK; } // [start, key), less than key, key exclusive status stree::get_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_below key<" << std::string(key.data(), key.size())); check_outside_tx(); auto pskey = pstring(key.data(), key.size()); internal::stree::btree_type::iterator it = my_btree->begin(); while (it != my_btree->end() && (*it).first < pskey) { auto ret = callback((*it).first.c_str(), (*it).first.size(), (*it).second.c_str(), (*it).second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; it++; } return status::OK; } // get between (key1, key2), key1 exclusive, key2 exclusive status stree::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { LOG("get_between key range=[" << std::string(key1.data(), key1.size()) << "," << std::string(key2.data(), key2.size()) << ")"); check_outside_tx(); auto pskey1 = pstring(key1.data(), key1.size()); auto pskey2 = pstring(key2.data(), key2.size()); internal::stree::btree_type::iterator it = my_btree->upper_bound(pskey1); while (it != my_btree->end() && (*it).first < pskey2) { auto ret = callback((*it).first.c_str(), (*it).first.size(), (*it).second.c_str(), (*it).second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; it++; } return status::OK; } status stree::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->find( pstring(key.data(), key.size())); if (it == my_btree->end()) { LOG(" key not found"); return status::NOT_FOUND; } return status::OK; } status stree::get(string_view key, get_v_callback *callback, void *arg) { LOG("get using callback for key=" << std::string(key.data(), key.size())); check_outside_tx(); internal::stree::btree_type::iterator it = my_btree->find( pstring(key.data(), key.size())); if (it == my_btree->end()) { LOG(" key not found"); return status::NOT_FOUND; } callback(it->second.c_str(), it->second.size(), arg); return status::OK; } status stree::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); check_outside_tx(); auto result = my_btree->insert(std::make_pair( pstring(key.data(), key.size()), pstring(value.data(), value.size()))); if (!result.second) { // key already exists, so update typename internal::stree::btree_type::value_type &entry = *result.first; transaction::manual tx(pmpool); conditional_add_to_tx(&(entry.second)); entry.second = std::string(value.data(), value.size()); transaction::commit(); } return status::OK; } status stree::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); auto result = my_btree->erase(std::string(key.data(), key.size())); return (result == 1) ? status::OK : status::NOT_FOUND; } void stree::Recover() { if (!OID_IS_NULL(*root_oid)) { my_btree = (internal::stree::btree_type *)pmemobj_direct(*root_oid); my_btree->garbage_collection(); } else { pmem::obj::transaction::manual tx(pmpool); pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent().raw(); pmem::obj::transaction::commit(); my_btree = (internal::stree::btree_type *)pmemobj_direct(*root_oid); } } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines-experimental/stree.h000066400000000000000000000067221361504041500210650ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../pmemobj_engine.h" #include "stree/persistent_b_tree.h" #include "stree/pstring.h" using pmem::obj::persistent_ptr; using pmem::obj::pool; namespace pmem { namespace kv { namespace internal { namespace stree { const size_t DEGREE = 64; const size_t MAX_KEY_SIZE = 20; const size_t MAX_VALUE_SIZE = 200; typedef persistent::b_tree, pstring, DEGREE> btree_type; } /* namespace stree */ } /* namespace internal */ class stree : public pmemobj_engine_base { public: stree(std::unique_ptr cfg); ~stree(); std::string name() final; status count_all(std::size_t &cnt) final; status count_above(string_view key, std::size_t &cnt) final; status count_equal_above(string_view key, std::size_t &cnt) final; status count_equal_below(string_view key, std::size_t &cnt) final; status count_below(string_view key, std::size_t &cnt) final; status count_between(string_view key1, string_view key2, std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status get_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_below(string_view key, get_kv_callback *callback, void *arg) final; status get_below(string_view key, get_kv_callback *callback, void *arg) final; status get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; private: stree(const stree &); void operator=(const stree &); void Recover(); internal::stree::btree_type *my_btree; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines-experimental/stree/000077500000000000000000000000001361504041500207055ustar00rootroot00000000000000pmemkv-1.1/src/engines-experimental/stree/persistent_b_tree.h000066400000000000000000001345241361504041500246070ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef PERSISTENT_B_TREE #define PERSISTENT_B_TREE #include #include #include #include #include #include #include #include #include #include #include #include #include namespace persistent { namespace internal { using namespace pmem::obj; class node_t { uint64_t _level; public: node_t(uint64_t level = 0) : _level(level) { } bool leaf() const { return _level == 0ull; } uint64_t level() const { return _level; } }; template class node_iterator { typedef TLeafNode leaf_node_type; typedef typename std::conditional::type leaf_node_ptr; friend class node_iterator; public: typedef typename leaf_node_type::value_type value_type; using iterator_category = std::random_access_iterator_tag; using difference_type = ptrdiff_t; typedef typename std::conditional::type reference; typedef typename std::conditional::type pointer; node_iterator() : node(nullptr), position(0) { } node_iterator(leaf_node_ptr node_ptr, size_t p) : node(node_ptr), position(p) { } node_iterator(const node_iterator &other) : node(other.node), position(other.position) { } template ::type> node_iterator(const node_iterator &other) : node(other.node), position(other.position) { } node_iterator &operator++() { ++position; return *this; } node_iterator operator++(int) { node_iterator tmp = *this; ++*this; return tmp; } node_iterator &operator--() { assert(position > 0); --position; return *this; } node_iterator operator--(int) { node_iterator tmp = *this; --*this; return tmp; } node_iterator operator+(size_t off) const { return node_iterator(node, position + off); } node_iterator operator+=(difference_type off) { position += static_cast(off); return *this; } node_iterator operator-(difference_type off) const { assert(node != nullptr); assert(position >= off); return node_iterator(node, position - off); } difference_type operator-(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return static_cast(position - other.position); } bool operator==(const node_iterator &other) const { return node == other.node && position == other.position; } bool operator!=(const node_iterator &other) const { return !(*this == other); } bool operator<(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return position < other.position; } bool operator>(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return position > other.position; } reference operator*() const { assert(node != nullptr); return (*node)[position]; } pointer operator->() const { return &**this; } private: leaf_node_ptr node; size_t position; }; template class leaf_node_t : public node_t { /** * Array of indexes. */ struct leaf_entries_t { leaf_entries_t() : _size(0) { for (uint64_t i = 0; i < number_entrys_slots; ++i) { idxs[i] = i; } } uint64_t idxs[number_entrys_slots]; size_t _size; }; public: typedef TKey key_type; typedef TValue mapped_type; typedef std::pair value_type; typedef value_type &reference; typedef const value_type &const_reference; typedef value_type *pointer; typedef const value_type *const_pointer; typedef node_iterator iterator; typedef node_iterator const_iterator; leaf_node_t(uint64_t e) : node_t(), epoch(e), consistent_id(0), p_consistent_id(0) { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); } leaf_node_t(uint64_t e, const_reference entry) : node_t(), epoch(e), consistent_id(0), p_consistent_id(0) { entries[0] = entry; consistent()->idxs[0] = 0; consistent()->_size = 1; assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); } leaf_node_t(uint64_t e, const_iterator first, const_iterator last, const persistent_ptr &_prev, const persistent_ptr &_next) : node_t(), epoch(e), consistent_id(0), prev(_prev), next(_next), p_consistent_id(0) { copy(first, last); assert(std::distance(first, last) >= 0); assert(size() == static_cast(std::distance(first, last))); assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); } leaf_node_t(uint64_t e, const_reference entry, const_iterator first, const_iterator last, const persistent_ptr &_prev, const persistent_ptr &_next) : node_t(), epoch(e), consistent_id(0), prev(_prev), next(_next), p_consistent_id(0) { copy_insert(entry, first, last); assert(std::distance(first, last) >= 0); assert(size() == static_cast(std::distance(first, last)) + 1); assert(std::binary_search(begin(), end(), entry, [](const_reference a, const_reference b) { return a.first < b.first; })); assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); } std::pair insert(pool_base &pop, const_reference entry) { return insert(pop, entry, this->begin(), this->end()); } iterator find(const key_type &key) { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); iterator it = std::lower_bound( begin(), end(), key, [](const_reference entry, const TKey &key) { return entry.first < key; }); if (it == end() || it->first == key) return it; else return end(); } const_iterator find(const key_type &key) const { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); const_iterator it = std::lower_bound( begin(), end(), key, [](const_reference entry, const TKey &key) { return entry.first < key; }); if (it == end() || it->first == key) return it; else return end(); } iterator lower_bound(const key_type &key) { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); iterator it = std::lower_bound( begin(), end(), key, [](const_reference entry, const TKey &key) { return entry.first < key; }); if (it == end() || it->first == key || it->first > key) return it; else return end(); } const_iterator lower_bound(const key_type &key) const { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); const_iterator it = std::lower_bound( begin(), end(), key, [](const_reference entry, const TKey &key) { return entry.first < key; }); if (it == end() || it->first == key || it->first > key) return it; else return end(); } iterator upper_bound(const key_type &key) { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); iterator it = std::upper_bound( begin(), end(), key, [](const TKey &key, const_reference entry) { return key < entry.first; }); if (it == end() || it->first > key) return it; else return end(); } const_iterator upper_bound(const key_type &key) const { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); const_iterator it = std::upper_bound( begin(), end(), key, [](const TKey &key, const_reference entry) { return key < entry.first; }); if (it == end() || it->first > key) return it; else return end(); } size_t erase(pool_base &pop, const key_type &key) { assert(std::is_sorted(begin(), end(), [](const_reference a, const_reference b) { return a.first < b.first; })); iterator it = find(key); if (it == end()) return size_t(0); internal_erase(pop, it); return size_t(1); } /** * Return begin iterator on an array of correct indices. */ iterator begin() { return iterator(this, 0); } /** * Return const_iterator to the beginning. */ const_iterator begin() const { return const_iterator(this, 0); } /** * Return end iterator on an array of indices. */ iterator end() { return iterator(this, consistent()->_size); } /** * Return const_iterator to the end. */ const_iterator end() const { return const_iterator(this, consistent()->_size); } /** * Return the size of the array of entries (key/value). */ size_t size() const { return consistent()->_size; } bool full() const { return size() == number_entrys_slots; } const_reference back() const { return entries[consistent()->idxs[consistent()->_size - 1]]; } reference at(size_t pos) { if (size() <= pos) { throw std::out_of_range( "Accessing incorrect element in leaf node"); } return entries[consistent()->idxs[pos]]; } const_reference at(size_t pos) const { if (size() <= pos) { throw std::out_of_range( "Accessing incorrect element in leaf node"); } return entries[consistent()->idxs[pos]]; } reference operator[](size_t pos) { return entries[consistent()->idxs[pos]]; } const_reference operator[](size_t pos) const { return entries[consistent()->idxs[pos]]; } const persistent_ptr &get_next() const { return this->next; } void set_next(const persistent_ptr &n) { this->next = n; } const persistent_ptr &get_prev() const { return this->prev; } void set_prev(const persistent_ptr &p) { this->prev = p; } void check_consistency(uint64_t global_epoch) { if (global_epoch != epoch) { consistent_id = p_consistent_id; epoch = global_epoch; } } private: uint64_t epoch; uint32_t consistent_id; persistent_ptr prev; persistent_ptr next; char padding[64]; value_type entries[number_entrys_slots]; leaf_entries_t v[2]; char padding1[64]; uint32_t p_consistent_id; leaf_entries_t *consistent() { assert(consistent_id < 2); return v + consistent_id; } const leaf_entries_t *consistent() const { assert(consistent_id < 2); return v + consistent_id; } leaf_entries_t *working_copy() { assert(consistent_id < 2); uint32_t working_id = 1 - consistent_id; return v + working_id; } void switch_consistent(pool_base &pop) { p_consistent_id = consistent_id = 1 - consistent_id; // TODO: need to check if it make sense to use non-temporal store pop.persist(&p_consistent_id, sizeof(p_consistent_id)); } /** * Insert new 'entry' in array of entries, update idxs. */ std::pair insert(pool_base &pop, const_reference entry, iterator begin, iterator end) { assert(!full()); iterator hint = std::lower_bound(begin, end, entry.first, [&](const_reference entry, const key_type &key) { return entry.first < key; }); if (hint != end && hint->first == entry.first) { return std::pair(hint, false); } size_t insert_pos = get_insert_idx(); assert(std::none_of( consistent()->idxs, consistent()->idxs + size(), [insert_pos](uint64_t idx) { return insert_pos == idx; })); // insert an entry to the end entries[insert_pos] = entry; pop.flush(&(entries[insert_pos]), sizeof(entries[insert_pos])); // update tmp idxs size_t position = insert_idx(pop, insert_pos, hint); // update consistent switch_consistent(pop); assert(std::is_sorted(this->begin(), this->end(), [](const_reference a, const_reference b) { return a.first < b.first; })); return std::pair(iterator(this, position), true); } size_t get_insert_idx() const { const leaf_entries_t *c = consistent(); return c->idxs[c->_size]; } size_t insert_idx(pool_base &pop, uint64_t new_entry_idx, iterator hint) { size_t size = this->size(); leaf_entries_t *tmp = working_copy(); auto in_begin = consistent()->idxs; auto in_end = in_begin + size; auto partition_point = in_begin + std::distance(this->begin(), hint); auto out_begin = tmp->idxs; auto insert_pos = std::copy(in_begin, partition_point, out_begin); *insert_pos = new_entry_idx; std::copy(partition_point, in_end, insert_pos + 1); tmp->_size = size + 1; #if 0 pop.flush( tmp->idxs, sizeof(tmp->idxs[0])*tmp->_size ); pop.persist( &(tmp->_size), sizeof(tmp->_size) ); #else pop.persist(tmp, sizeof(leaf_entries_t)); #endif auto result = std::distance(out_begin, insert_pos); assert(result >= 0); return static_cast(result); } void remove_idx(pool_base &pop, iterator it) { size_t size = this->size(); leaf_entries_t *tmp = working_copy(); auto in_begin = consistent()->idxs; auto in_end = in_begin + size; auto partition_point = in_begin + std::distance(this->begin(), it); auto out = tmp->idxs; out = std::copy(in_begin, partition_point, out); out = std::copy(partition_point + 1, in_end, out); *out = *partition_point; tmp->_size = size - 1; pop.persist(tmp, sizeof(leaf_entries_t)); } /** * Copy entries from another node in the range of [first, last) and insert new * entry. */ void copy_insert(const_reference entry, const_iterator first, const_iterator last) { assert(std::distance(first, last) >= 0); assert(static_cast(std::distance(first, last)) < number_entrys_slots); auto d_last = std::merge(first, last, &entry, &entry + 1, entries, [](const_reference a, const_reference b) { return a.first < b.first; }); assert(std::distance(entries, d_last) >= 0); consistent()->_size = static_cast(std::distance(entries, d_last)); std::iota(consistent()->idxs, consistent()->idxs + consistent()->_size, 0); } /** * Copy entries from another node in the range of [first, last). */ void copy(const_iterator first, const_iterator last) { assert(std::distance(first, last) >= 0); assert(static_cast(std::distance(first, last)) < number_entrys_slots); auto d_last = std::copy(first, last, entries); assert(std::distance(entries, d_last) >= 0); consistent()->_size = static_cast(std::distance(entries, d_last)); std::iota(consistent()->idxs, consistent()->idxs + consistent()->_size, 0); } /** * Remove element pointed by iterator. */ void internal_erase(pool_base &pop, iterator it) { // update tmp idxs remove_idx(pop, it); // update consistent switch_consistent(pop); assert(std::is_sorted(this->begin(), this->end(), [](const_reference a, const_reference b) { return a.first < b.first; })); } }; // class leaf_node_t template class inner_node_t : public node_t { typedef inner_node_t self_type; public: typedef TKey key_type; typedef key_type value_type; // Inner node stores only keys typedef value_type &reference; typedef const value_type &const_reference; typedef value_type *pointer; typedef const value_type *const_pointer; typedef node_iterator iterator; typedef node_iterator const_iterator; private: const static size_t number_children_slots = number_entrys_slots + 1; struct inner_entries_t { value_type entries[number_entrys_slots]; persistent_ptr children[number_children_slots]; size_t _size = 0; }; inner_entries_t v[2]; uint32_t consistent_id; inner_entries_t *consistent() { assert(consistent_id < 2); return v + consistent_id; } inner_entries_t *working_copy() { assert(consistent_id < 2); uint32_t working_id = 1 - consistent_id; return v + working_id; } const inner_entries_t *consistent() const { assert(consistent_id < 2); return v + consistent_id; } void switch_consistent(pool_base &pop) { consistent_id = 1 - consistent_id; pop.persist(&consistent_id, sizeof(consistent_id)); } public: using difference_type = ptrdiff_t; inner_node_t(size_t level, const value_type &key, const persistent_ptr &child_0, const persistent_ptr &child_1) : node_t(level), consistent_id(0) { inner_entries_t *consist = consistent(); consist->entries[0] = key; consist->_size++; consist->children[0] = child_0; consist->children[1] = child_1; assert(std::is_sorted(begin(), end())); } inner_node_t(size_t level, const_iterator first, const_iterator last, const inner_node_t *src) : node_t(level), consistent_id(0) { auto o_last = std::copy(first, last, consistent()->entries); auto result = std::distance(consistent()->entries, o_last); assert(result >= 0); consistent()->_size = static_cast(result); auto in_cbegin = std::next(src->consistent()->children, std::distance(src->begin(), first)); auto in_cend = std::next( in_cbegin, static_cast(consistent()->_size + 1)); std::copy(in_cbegin, in_cend, consistent()->children); assert(std::is_sorted(begin(), end())); } /** * Update split node with pair of new nodes */ void update_splitted_child(pool_base &pop, const_reference entry, persistent_ptr &lnode, persistent_ptr &rnode, const persistent_ptr &splitted_node) { assert(!full()); iterator partition_point = std::lower_bound(this->begin(), this->end(), entry); // Insert new key auto in_entries_begin = consistent()->entries; auto in_entries_end = std::next(in_entries_begin, static_cast(consistent()->_size)); auto in_entries_splitted = std::next( in_entries_begin, std::distance(this->begin(), partition_point)); auto out_entries_begin = working_copy()->entries; auto insert_pos = std::copy(in_entries_begin, in_entries_splitted, out_entries_begin); *insert_pos = entry; auto out_entries_end = std::copy(in_entries_splitted, in_entries_end, ++insert_pos); auto result_entries = std::distance(out_entries_begin, out_entries_end); assert(result_entries >= 0); working_copy()->_size = static_cast(result_entries); pop.flush(working_copy()->entries, sizeof(working_copy()->entries[0]) * working_copy()->_size); pop.flush(&(working_copy()->_size), sizeof(working_copy()->_size)); // Update children auto in_children_begin = consistent()->children; auto in_children_splitted = std::next( in_children_begin, std::distance(this->begin(), partition_point)); auto in_children_end = std::next(in_children_begin, static_cast(consistent()->_size + 1)); auto out_children_begin = working_copy()->children; assert(*in_children_splitted == splitted_node); auto out_insert_pos = std::copy(in_children_begin, in_children_splitted, out_children_begin); *out_insert_pos++ = lnode; *out_insert_pos++ = rnode; auto out_children_end = std::copy(++in_children_splitted, in_children_end, out_insert_pos); auto result_children = std::distance(out_children_begin, out_children_end); assert(result_children >= 0); pop.flush(working_copy()->children, sizeof(working_copy()->children[0]) * static_cast(result_children)); switch_consistent(pop); assert(std::is_sorted(this->begin(), this->end())); } const persistent_ptr &get_child(const_reference key) const { auto it = std::lower_bound(this->begin(), this->end(), key); return get_left_child(it); } const persistent_ptr &get_left_child(const_iterator it) const { auto result = std::distance(this->begin(), it); assert(result >= 0); size_t child_pos = static_cast(result); return this->consistent()->children[child_pos]; } const persistent_ptr &get_right_child(const_iterator it) const { size_t child_pos = std::distance(this->begin(), it) + 1; return this->consistent()->children[child_pos]; } bool full() const { return this->size() == number_entrys_slots; } /** * Return begin iterator on an array of keys. */ iterator begin() { return iterator(this, 0); } const_iterator begin() const { return const_iterator(this, 0); } /** * Return end iterator on an array of keys. */ iterator end() { return begin() + this->size(); } const_iterator end() const { return begin() + this->size(); } /** * Return the size of the array of keys. */ size_t size() const { return consistent()->_size; } const_reference back() const { return consistent()->entries[this->size() - 1]; } reference at(size_t pos) { if (size() <= pos) { throw std::out_of_range( "Accessing incorrect element in inner node"); } return consistent()->entries[pos]; } const_reference at(size_t pos) const { if (size() <= pos) { throw std::out_of_range("Accessing incorrect element inner node"); } return consistent()->entries[pos]; } reference operator[](size_t pos) { return consistent()->entries[pos]; } const_reference operator[](size_t pos) const { return consistent()->entries[pos]; } }; // class inner_node_t template class b_tree_iterator { private: typedef LeafNode leaf_node_type; typedef typename std::conditional::type leaf_node_ptr; typedef typename std::conditional< is_const, typename leaf_node_type::const_iterator, typename leaf_node_type::iterator>::type leaf_iterator; friend class b_tree_iterator; public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = ptrdiff_t; typedef typename leaf_iterator::value_type value_type; typedef typename leaf_iterator::reference reference; typedef typename leaf_iterator::pointer pointer; b_tree_iterator(std::nullptr_t) : current_node(nullptr), leaf_it() { } b_tree_iterator(leaf_node_ptr node) : current_node(node), leaf_it(node->begin()) { } b_tree_iterator(leaf_node_ptr node, leaf_iterator _leaf_it) : current_node(node), leaf_it(_leaf_it) { } b_tree_iterator(const b_tree_iterator &other) : current_node(other.current_node), leaf_it(other.leaf_it) { } template ::type> b_tree_iterator(const b_tree_iterator &other) : current_node(other.current_node), leaf_it(other.leaf_it) { } b_tree_iterator &operator=(const b_tree_iterator &other) { current_node = other.current_node; leaf_it = other.leaf_it; return *this; } b_tree_iterator &operator++() { ++leaf_it; if (leaf_it == current_node->end()) { leaf_node_ptr tmp = current_node->get_next().get(); if (tmp) { current_node = tmp; leaf_it = current_node->begin(); } } return *this; } b_tree_iterator operator++(int) { b_tree_iterator tmp = *this; ++*this; return tmp; } b_tree_iterator &operator--() { if (leaf_it == current_node->begin()) { leaf_node_ptr tmp = current_node->get_prev().get(); if (tmp) { current_node = tmp; leaf_it = current_node->last(); } } else { --leaf_it; } return *this; } b_tree_iterator operator--(int) { b_tree_iterator tmp = *this; --*this; return tmp; } bool operator==(const b_tree_iterator &other) { return current_node == other.current_node && leaf_it == other.leaf_it; } bool operator!=(const b_tree_iterator &other) { return !(*this == other); } reference operator*() const { return *(leaf_it); } pointer operator->() const { return &**this; } private: leaf_node_ptr current_node; leaf_iterator leaf_it; }; // class b_tree_iterator template class b_tree_base { const static size_t number_entrys_slots = degree - 1; const static size_t number_children_slots = degree; typedef leaf_node_t leaf_node_type; typedef inner_node_t inner_node_type; typedef persistent_ptr node_persistent_ptr; typedef persistent_ptr leaf_node_persistent_ptr; typedef persistent_ptr inner_node_persistent_ptr; public: typedef b_tree_base self_type; typedef typename leaf_node_type::value_type value_type; typedef typename leaf_node_type::key_type key_type; typedef typename leaf_node_type::mapped_type mapped_type; typedef typename leaf_node_type::reference reference; typedef typename leaf_node_type::const_reference const_reference; typedef typename leaf_node_type::pointer pointer; typedef typename leaf_node_type::const_pointer const_pointer; typedef b_tree_iterator iterator; typedef b_tree_iterator const_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; private: static const key_type &get_last_key(const node_persistent_ptr &node) { if (node->leaf()) { return cast_leaf(node.get())->back().first; } else { return cast_inner(node.get())->back(); } } uint64_t epoch; persistent_ptr root; /** * Splitting node. */ persistent_ptr split_node; /** * Left and right node during split. */ persistent_ptr left_child; persistent_ptr right_child; void create_new_root(pool_base &, const key_type &, node_persistent_ptr &, node_persistent_ptr &); std::pair insert_descend(pool_base &, const_reference); typename inner_node_type::const_iterator split_half(pool_base &pop, persistent_ptr &node, persistent_ptr &left, persistent_ptr &right) { assert(split_node == node); inner_node_type *inner = cast_inner(node).get(); typename inner_node_type::const_iterator middle = inner->begin() + inner->size() / 2; typename inner_node_type::const_iterator l_begin = inner->begin(); typename inner_node_type::const_iterator l_end = middle; typename inner_node_type::const_iterator r_begin = middle + 1; typename inner_node_type::const_iterator r_end = inner->end(); allocate_inner(pop, left, inner->level(), l_begin, l_end, inner); allocate_inner(pop, right, inner->level(), r_begin, r_end, inner); return middle; } void split_inner_node(pool_base &pop, const node_persistent_ptr &src_node, inner_node_type *parent_node, node_persistent_ptr &left, node_persistent_ptr &right) { assert(split_node == nullptr); assignment(pop, split_node, src_node); typename inner_node_type::const_iterator partition_point = split_half(pop, split_node, left, right); assert(partition_point != cast_inner(split_node)->end()); if (parent_node) { parent_node->update_splitted_child(pop, *partition_point, left, right, split_node); } else { // Root node is split assert(root == split_node); create_new_root(pop, *partition_point, left, right); } deallocate(split_node); } iterator split_leaf_node(pool_base &, inner_node_type *, persistent_ptr &, const_reference, persistent_ptr &, persistent_ptr &); static bool is_left_node(const leaf_node_type *src_node, const leaf_node_type *lnode) { assert(src_node); assert(lnode); typename leaf_node_type::const_iterator middle = src_node->begin() + src_node->size() / 2; return std::includes(lnode->begin(), lnode->end(), src_node->begin(), middle); } static bool is_right_node(const leaf_node_type *src_node, const leaf_node_type *rnode) { assert(src_node); assert(rnode); typename leaf_node_type::const_iterator middle = src_node->begin() + src_node->size() / 2; return std::includes(rnode->begin(), rnode->end(), middle, src_node->end()); } void repair_leaf_split(pool_base &pop) { assert(root != nullptr); assert(split_node != nullptr); assert(split_node->leaf()); const key_type &key = get_last_key(split_node); path_type path; leaf_node_persistent_ptr found_node = find_leaf_to_insert(key, path); assert(path[0] == root); if (split_node == found_node) { // Split not completed const leaf_node_type *split_leaf = cast_leaf(split_node).get(); leaf_node_type *lnode = cast_leaf(left_child).get(); leaf_node_type *rnode = cast_leaf(right_child).get(); if (left_child && is_left_node(split_leaf, lnode)) { if (right_child && is_right_node( split_leaf, rnode)) { // Both children were allocated // during split before crash inner_node_type *parent_node = path.empty() ? nullptr : path.back().get(); lnode->set_next(cast_leaf(right_child)); pop.persist(lnode->get_next()); correct_leaf_node_links(pop, split_node, left_child, right_child); if (parent_node) { parent_node->update_splitted_child( pop, lnode->back().first, left_child, right_child, split_node); } else { create_new_root(pop, lnode->back().first, left_child, right_child); } } else { // Only left child was allocated during split // before crash deallocate(left_child); } } } else { // split_node was replaced by two new (left and right) nodes. // Need to deallocate split_node deallocate(split_node); } split_node = nullptr; } void repair_inner_split(pool_base &pop) { assert(root != nullptr); assert(!root->leaf()); assert(split_node != nullptr); assert(!split_node->leaf()); const key_type &key = get_last_key(split_node); path_type path; find_leaf_to_insert(key, path); assert(path[0] == root); uint64_t root_level = path[0]->level(); uint64_t split_level = split_node->level(); assert(split_level <= root_level); if (split_node == path[root_level - split_level]) { // Split not completed // we could simply roll back const inner_node_type *inner = cast_inner(split_node).get(); typename inner_node_type::const_iterator middle = inner->begin() + inner->size() / 2; if (left_child && !(left_child->leaf()) && std::equal(inner->begin(), middle, cast_inner(left_child)->begin())) { deallocate(left_child); } if (right_child && !(right_child->leaf()) && std::equal(middle + 1, inner->end(), cast_inner(right_child)->begin())) { deallocate(right_child); } } else { // split_node was replaced by two new (left and right) nodes. // Need to deallocate split_node deallocate(split_node); } split_node = nullptr; } void correct_leaf_node_links(pool_base &, persistent_ptr &, persistent_ptr &, persistent_ptr &); void assignment(pool_base &pop, persistent_ptr &lhs, const persistent_ptr &rhs) { // lhs.raw_ptr()->off = rhs.raw_ptr()->off; lhs = rhs; pop.persist(lhs); } leaf_node_type *find_leaf_node(const key_type &key) const { if (root == nullptr) return nullptr; node_persistent_ptr node = root; while (!node->leaf()) { node = cast_inner(node)->get_child(key); } leaf_node_type *leaf = cast_leaf(node).get(); leaf->check_consistency(epoch); return leaf; } // TODO: merge with previous method typedef std::vector path_type; leaf_node_persistent_ptr find_leaf_to_insert(const key_type &key, path_type &path) const { assert(root != nullptr); node_persistent_ptr node = root; while (!node->leaf()) { path.push_back(cast_inner(node)); node = cast_inner(node)->get_child(key); } leaf_node_persistent_ptr leaf = cast_leaf(node); leaf->check_consistency(epoch); return leaf; } typename path_type::const_iterator find_full_node(const path_type &path) { auto i = path.end() - 1; for (; i > path.begin(); --i) { if (!(*i)->full()) return i; } return i; } leaf_node_type *leftmost_leaf() const { if (root == nullptr) return nullptr; node_persistent_ptr node = root; while (!node->leaf()) { inner_node_type *inner_node = cast_inner(node).get(); node = inner_node->get_left_child(inner_node->begin()); } leaf_node_type *leaf = cast_leaf(node).get(); leaf->check_consistency(epoch); return leaf; } leaf_node_type *rightmost_leaf() const { if (root == nullptr) return nullptr; node_persistent_ptr node = root; while (!node->leaf()) { inner_node_type *inner_node = cast_inner(node).get(); node = inner_node->get_left_child(inner_node->end()); } leaf_node_type *leaf = cast_leaf(node).get(); leaf->check_consistency(epoch); return leaf; } static persistent_ptr &cast_inner(persistent_ptr &node) { return reinterpret_cast &>(node); } static inner_node_type *cast_inner(node_t *node) { return static_cast(node); } static persistent_ptr &cast_leaf(persistent_ptr &node) { return reinterpret_cast &>(node); } static leaf_node_type *cast_leaf(node_t *node) { return static_cast(node); } template inline persistent_ptr allocate_inner(pool_base &pop, persistent_ptr &node, Args &&... args) { make_persistent_atomic(pop, cast_inner(node), args...); return cast_inner(node); } template inline persistent_ptr allocate_leaf(pool_base &pop, persistent_ptr &node, Args &&... args) { make_persistent_atomic(pop, cast_leaf(node), epoch, args...); return cast_leaf(node); } inline void deallocate(persistent_ptr &node) { if (node == nullptr) return; pool_base pop = get_pool_base(); transaction::manual tx(pop); if (node->leaf()) { deallocate_leaf(cast_leaf(node)); } else { deallocate_inner(cast_inner(node)); } node = nullptr; transaction::commit(); } inline void deallocate_inner(inner_node_persistent_ptr &node) { delete_persistent(node); } inline void deallocate_leaf(leaf_node_persistent_ptr &node) { delete_persistent(node); } PMEMobjpool *get_objpool() { PMEMoid oid = pmemobj_oid(this); return pmemobj_pool_by_oid(oid); } pool_base get_pool_base() { return pool_base(get_objpool()); } public: b_tree_base() : epoch(0) { } std::pair insert(const_reference entry) { auto pop = get_pool_base(); if (root == nullptr) { allocate_leaf(pop, root); } assert(root != nullptr); std::pair ret = insert_descend(pop, entry); return ret; } iterator find(const key_type &key) { leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::iterator leaf_it = leaf->find(key); if (leaf->end() == leaf_it) return end(); return iterator(leaf, leaf_it); } const_iterator find(const key_type &key) const { const leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::const_iterator leaf_it = leaf->find(key); if (leaf->end() == leaf_it) return end(); return const_iterator(leaf, leaf_it); } /** * Returns an iterator pointing to the least element which is larger than or equal * to the given key. Keys are sorted in lexicographical order (see * std::lexicographical_compare). * * @param[in] key sets the lower bound (inclusive) * * @return iterator */ iterator lower_bound(const key_type &key) { leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::iterator leaf_it = leaf->lower_bound(key); if (leaf->end() == leaf_it) return end(); return iterator(leaf, leaf_it); } /** * Returns a const iterator pointing to the least element which is larger than or * equal to the given key. Keys are sorted in lexicographical order (see * std::lexicographical_compare). * * @param[in] key sets the lower bound (inclusive) * * @return const_iterator */ const_iterator lower_bound(const key_type &key) const { const leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::const_iterator leaf_it = leaf->lower_bound(key); if (leaf->end() == leaf_it) return end(); return const_iterator(leaf, leaf_it); } /** * Returns an iterator pointing to the least element which is larger than the * given key. Keys are sorted in lexicographical order (see * std::lexicographical_compare). * * @param[in] key sets the lower bound (exclusive) * * @return iterator */ iterator upper_bound(const key_type &key) { leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::iterator leaf_it = leaf->upper_bound(key); if (leaf->end() == leaf_it) return end(); return iterator(leaf, leaf_it); } /** * Returns a const iterator pointing to the least element which is larger than the * given key. Keys are sorted in lexicographical order (see * std::lexicographical_compare). * * @param[in] key sets the lower bound (exclusive) * * @return const_iterator */ const_iterator upper_bound(const key_type &key) const { const leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return end(); typename leaf_node_type::const_iterator leaf_it = leaf->upper_bound(key); if (leaf->end() == leaf_it) return end(); return const_iterator(leaf, leaf_it); } size_t erase(const key_type &key) { leaf_node_type *leaf = find_leaf_node(key); if (leaf == nullptr) return size_t(0); auto pop = get_pool_base(); return leaf->erase(pop, key); } void garbage_collection(); iterator begin() { return iterator(leftmost_leaf()); } iterator end() { leaf_node_type *leaf = rightmost_leaf(); return iterator(leaf, leaf ? leaf->end() : typename leaf_node_type::iterator()); } const_iterator begin() const { return const_iterator(leftmost_leaf()); } const_iterator end() const { const leaf_node_type *leaf = rightmost_leaf(); return const_iterator(leaf, leaf ? leaf->end() : typename leaf_node_type::const_iterator()); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } }; // class b_tree_base template void b_tree_base::garbage_collection() { pool_base pop = get_pool_base(); ++epoch; // pop.persist( &epoch, sizeof(epoch) ); if (split_node != nullptr) { if (split_node->leaf()) { repair_leaf_split(pop); } else { repair_inner_split(pop); } } } template typename b_tree_base::iterator b_tree_base::split_leaf_node(pool_base &pop, inner_node_type *parent_node, persistent_ptr &src_node, const_reference entry, persistent_ptr &left, persistent_ptr &right) { const leaf_node_type *split_leaf = cast_leaf(src_node).get(); assert(split_leaf->full()); assignment(pop, split_node, src_node); typename leaf_node_type::const_iterator middle = split_leaf->begin() + split_leaf->size() / 2; leaf_node_type *insert_node = nullptr; leaf_node_type *lnode = nullptr; if (entry.first < middle->first) { lnode = insert_node = allocate_leaf(pop, left, entry, split_leaf->begin(), middle, split_leaf->get_prev(), nullptr) .get(); allocate_leaf(pop, right, middle, split_leaf->end(), cast_leaf(left), split_leaf->get_next()) .get(); } else { lnode = allocate_leaf(pop, left, split_leaf->begin(), middle, split_leaf->get_prev(), nullptr) .get(); insert_node = allocate_leaf(pop, right, entry, middle, split_leaf->end(), cast_leaf(left), split_leaf->get_next()) .get(); } lnode->set_next(cast_leaf(right)); pop.persist(lnode->get_next()); correct_leaf_node_links(pop, src_node, left, right); if (parent_node) { parent_node->update_splitted_child(pop, lnode->back().first, left, right, split_node); } else { create_new_root(pop, lnode->back().first, left, right); } deallocate(split_node); typename leaf_node_type::iterator leaf_it = insert_node->find(entry.first); assert(leaf_it != insert_node->end()); assert(leaf_it->first == entry.first); assert(leaf_it->second == entry.second); return iterator(insert_node, leaf_it); } template void b_tree_base::correct_leaf_node_links( pool_base &pop, persistent_ptr &src_node, persistent_ptr &left, persistent_ptr &right) { persistent_ptr lnode = cast_leaf(left); persistent_ptr rnode = cast_leaf(right); leaf_node_type *current_node = cast_leaf(src_node).get(); if (current_node->get_prev() != nullptr) { current_node->get_prev()->set_next(lnode); pop.persist(current_node->get_prev()->get_next()); } if (current_node->get_next() != nullptr) { current_node->get_next()->set_prev(rnode); pop.persist(current_node->get_next()->get_prev()); } } template void b_tree_base::create_new_root(pool_base &pop, const key_type &key, node_persistent_ptr &l_child, node_persistent_ptr &r_child) { assert(l_child != nullptr); assert(r_child != nullptr); assert(split_node == root); allocate_inner(pop, root, root->level() + 1, key, l_child, r_child); } template std::pair::iterator, bool> b_tree_base::insert_descend(pool_base &pop, const_reference entry) { path_type path; const key_type &key = entry.first; node_persistent_ptr node = find_leaf_to_insert(key, path); leaf_node_type *leaf = cast_leaf(node).get(); inner_node_type *parent_node = nullptr; if (leaf->full()) { typename leaf_node_type::iterator leaf_it = leaf->find(key); if (leaf_it != leaf->end()) { // Entry with the same key found return std::pair(iterator(leaf, leaf_it), false); } /** * If root is leaf. */ if (path.empty()) { iterator it = split_leaf_node(pop, nullptr, node, entry, left_child, right_child); return std::pair(it, true); } // find the first full node auto i = find_full_node(path); /** * If root is full. Split root */ if ((*i)->full()) { parent_node = nullptr; split_inner_node(pop, *i, parent_node, left_child, right_child); parent_node = cast_inner(cast_inner(root)->get_child(key).get()); } else { parent_node = (*i).get(); } ++i; for (; i != path.end(); ++i) { split_inner_node(pop, *i, parent_node, left_child, right_child); parent_node = cast_inner(parent_node->get_child(key).get()); } iterator it = split_leaf_node(pop, parent_node, node, entry, left_child, right_child); return std::pair(it, true); } std::pair ret = leaf->insert(pop, entry); return std::pair(iterator(leaf, ret.first), ret.second); ; } } // namespace internal // TODO: add key comparator as a template argument; std::less should be default template class b_tree : public internal::b_tree_base { // Base type definitions typedef b_tree self_type; typedef internal::b_tree_base base_type; public: using base_type::begin; using base_type::end; using base_type::erase; using base_type::find; using base_type::insert; // Type definitions typedef typename base_type::key_type key_type; typedef typename base_type::mapped_type mapped_type; typedef typename base_type::value_type value_type; typedef typename base_type::iterator iterator; typedef typename base_type::const_iterator const_iterator; typedef typename base_type::reverse_iterator reverse_iterator; explicit b_tree() : base_type() { } ~b_tree() { } b_tree(const b_tree &) = delete; b_tree &operator=(const b_tree &) = delete; }; } // namespace persistent #endif // PERSISTENT_B_TREE pmemkv-1.1/src/engines-experimental/stree/pstring.h000066400000000000000000000066201361504041500225500ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef PERSISTENT_PSTRING_H #define PERSISTENT_PSTRING_H #include #include template class pstring { static const size_t BUFFER_SIZE = CAPACITY + 1; public: pstring(const std::string &s = "\0") { init(s.c_str(), s.size()); } pstring(const char *data, size_t size) { init(data, size); } pstring(const pstring &other) { init(other.c_str(), other.size()); } pstring &operator=(const pstring &other) { init(other.c_str(), other.size()); return *this; } pstring &operator=(const std::string &s) { init(s.c_str(), s.size()); return *this; } const char *c_str() const { return str; } size_t size() const { return _size; } char *begin() { return str; } char *end() { return str + _size; } const char *begin() const { return str; } const char *end() const { return str + _size; } private: void init(const char *src, size_t size) { if (size > CAPACITY) throw std::length_error("size exceed pstring capacity"); memcpy(str, src, size); str[size] = '\0'; _size = size; } char str[BUFFER_SIZE]; size_t _size; }; template inline bool operator<(const pstring &lhs, const pstring &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator>(const pstring &lhs, const pstring &rhs) { return std::lexicographical_compare(rhs.begin(), rhs.end(), lhs.begin(), lhs.end()); } template inline bool operator==(const pstring &lhs, const pstring &rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template std::ostream &operator<<(std::ostream &os, const pstring &obj) { return os << obj.c_str(); } #endif // PERSISTENT_PSTRING_H pmemkv-1.1/src/engines-experimental/tree3.cc000066400000000000000000000476001361504041500211230ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "tree3.h" #include "../out.h" #include #include #include #include #include #include namespace pmem { namespace kv { tree3::tree3(std::unique_ptr cfg) : pmemobj_engine_base(cfg) { Recover(); LOG("Started ok"); } tree3::~tree3() { LOG("Stopped ok"); } std::string tree3::name() { return "tree3"; } // =============================================================================================== // KEY/VALUE METHODS // =============================================================================================== status tree3::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); std::size_t result = 0; auto leaf = (pmem::kv::internal::tree3::KVLeaf *)pmemobj_direct(*root_oid); while (leaf) { for (int slot = LEAF_KEYS; slot--;) { auto kvslot = leaf->slots[slot].get_ro(); if (kvslot.empty() || kvslot.hash() == 0) continue; result++; } leaf = leaf->next.get(); // advance to next linked leaf } cnt = result; return status::OK; } status tree3::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); auto leaf = (pmem::kv::internal::tree3::KVLeaf *)pmemobj_direct(*root_oid); while (leaf) { for (int slot = LEAF_KEYS; slot--;) { auto kvslot = leaf->slots[slot].get_ro(); if (kvslot.empty() || kvslot.hash() == 0) continue; auto ret = callback(kvslot.key(), kvslot.get_ks(), kvslot.val(), kvslot.get_vs(), arg); if (ret != 0) return status::STOPPED_BY_CB; } leaf = leaf->next.get(); // advance to next linked leaf } return status::OK; } status tree3::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); // XXX - do not create temporary string auto leafnode = LeafSearch(std::string(key.data(), key.size())); if (leafnode) { const uint8_t hash = PearsonHash(key.data(), key.size()); for (int slot = LEAF_KEYS; slot--;) { if (leafnode->hashes[slot] == hash) { if (leafnode->keys[slot].compare( std::string(key.data(), key.size())) == 0) return status::OK; } } } LOG(" could not find key"); return status::NOT_FOUND; } status tree3::get(string_view key, get_v_callback *callback, void *arg) { LOG("get using callback for key=" << std::string(key.data(), key.size())); check_outside_tx(); // XXX - do not create temporary string auto leafnode = LeafSearch(std::string(key.data(), key.size())); if (leafnode) { const uint8_t hash = PearsonHash(key.data(), key.size()); for (int slot = LEAF_KEYS; slot--;) { if (leafnode->hashes[slot] == hash) { LOG(" found hash match, slot=" << slot); if (leafnode->keys[slot].compare( std::string(key.data(), key.size())) == 0) { auto kv = leafnode->leaf->slots[slot].get_ro(); LOG(" found value, slot=" << slot << ", size=" << std::to_string(kv.valsize())); callback(kv.val(), kv.valsize(), arg); return status::OK; } } } } LOG(" could not find key"); return status::NOT_FOUND; } status tree3::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); check_outside_tx(); const auto hash = PearsonHash(key.data(), key.size()); // XXX - do not create temporary string auto leafnode = LeafSearch(std::string(key.data(), key.size())); if (!leafnode) { LOG(" adding head leaf"); unique_ptr new_node( new internal::tree3::KVLeafNode()); new_node->is_leaf = true; transaction::run(pmpool, [&] { if (!leaves_prealloc.empty()) { new_node->leaf = leaves_prealloc.back(); leaves_prealloc.pop_back(); } else { auto old_head = persistent_ptr( *root_oid); auto new_leaf = make_persistent(); transaction::snapshot(root_oid); *root_oid = new_leaf.raw(); new_leaf->next = old_head; new_node->leaf = new_leaf; } LeafFillSpecificSlot(new_node.get(), hash, std::string(key.data(), key.size()), std::string(value.data(), value.size()), 0); }); tree_top = move(new_node); } else if (LeafFillSlotForKey(leafnode, hash, std::string(key.data(), key.size()), std::string(value.data(), value.size()))) { // nothing else to do } else { // XXX - do not create temporary string LeafSplitFull(leafnode, hash, std::string(key.data(), key.size()), std::string(value.data(), value.size())); } return status::OK; } status tree3::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); // XXX - do not create temporary string auto leafnode = LeafSearch(std::string(key.data(), key.size())); if (!leafnode) { LOG(" head not present"); return status::NOT_FOUND; } const auto hash = PearsonHash(key.data(), key.size()); for (int slot = LEAF_KEYS; slot--;) { if (leafnode->hashes[slot] == hash) { if (leafnode->keys[slot].compare( std::string(key.data(), key.size())) == 0) { LOG(" freeing slot=" << slot); leafnode->hashes[slot] = 0; leafnode->keys[slot].clear(); auto leaf = leafnode->leaf; transaction::run(pmpool, [&] { leaf->slots[slot].get_rw().clear(); }); return status::OK; // no duplicate keys allowed } } } return status::NOT_FOUND; } // =============================================================================================== // PROTECTED LEAF METHODS // =============================================================================================== internal::tree3::KVLeafNode *tree3::LeafSearch(const std::string &key) { internal::tree3::KVNode *node = tree_top.get(); if (node == nullptr) return nullptr; bool matched; while (!node->is_leaf) { matched = false; auto inner = (internal::tree3::KVInnerNode *)node; #ifndef NDEBUG inner->assert_invariants(); #endif const uint8_t keycount = inner->keycount; for (uint8_t idx = 0; idx < keycount; idx++) { node = inner->children[idx].get(); if (key.compare(inner->keys[idx]) <= 0) { matched = true; break; } } if (!matched) node = inner->children[keycount].get(); } return (internal::tree3::KVLeafNode *)node; } void tree3::LeafFillEmptySlot(internal::tree3::KVLeafNode *leafnode, const uint8_t hash, const std::string &key, const std::string &value) { for (int slot = LEAF_KEYS; slot--;) { if (leafnode->hashes[slot] == 0) { LeafFillSpecificSlot(leafnode, hash, key, value, slot); return; } } } bool tree3::LeafFillSlotForKey(internal::tree3::KVLeafNode *leafnode, const uint8_t hash, const std::string &key, const std::string &value) { // scan for empty/matching slots int last_empty_slot = -1; int key_match_slot = -1; for (int slot = LEAF_KEYS; slot--;) { auto slot_hash = leafnode->hashes[slot]; if (slot_hash == 0) { last_empty_slot = slot; } else if (slot_hash == hash) { if (leafnode->keys[slot].compare(key) == 0) { key_match_slot = slot; break; // no duplicate keys allowed } } } // update suitable slot if found int slot = key_match_slot >= 0 ? key_match_slot : last_empty_slot; if (slot >= 0) { LOG(" filling slot=" << slot); transaction::run(pmpool, [&] { LeafFillSpecificSlot(leafnode, hash, key, value, slot); }); } return slot >= 0; } void tree3::LeafFillSpecificSlot(internal::tree3::KVLeafNode *leafnode, const uint8_t hash, const std::string &key, const std::string &value, const int slot) { leafnode->leaf->slots[slot].get_rw().set(hash, key, value); leafnode->hashes[slot] = hash; leafnode->keys[slot] = key; } void tree3::LeafSplitFull(internal::tree3::KVLeafNode *leafnode, const uint8_t hash, const std::string &key, const std::string &value) { std::string keys[LEAF_KEYS + 1]; keys[LEAF_KEYS] = key; for (int slot = LEAF_KEYS; slot--;) keys[slot] = leafnode->keys[slot]; std::sort(std::begin(keys), std::end(keys), [](const std::string &lhs, const std::string &rhs) { return lhs.compare(rhs) < 0; }); std::string split_key = keys[LEAF_KEYS_MIDPOINT]; LOG(" splitting leaf at key=" << split_key); // split leaf into two leaves, moving slots that sort above split key to new leaf unique_ptr new_leafnode( new internal::tree3::KVLeafNode()); new_leafnode->parent = leafnode->parent; new_leafnode->is_leaf = true; transaction::run(pmpool, [&] { persistent_ptr new_leaf; if (!leaves_prealloc.empty()) { new_leaf = leaves_prealloc.back(); new_leafnode->leaf = new_leaf; leaves_prealloc.pop_back(); } else { auto old_head = persistent_ptr(*root_oid); new_leaf = make_persistent(); transaction::snapshot(root_oid); *root_oid = new_leaf.raw(); new_leaf->next = old_head; new_leafnode->leaf = new_leaf; } for (int slot = LEAF_KEYS; slot--;) { if (leafnode->keys[slot].compare(split_key) > 0) { new_leaf->slots[slot].swap(leafnode->leaf->slots[slot]); new_leafnode->hashes[slot] = leafnode->hashes[slot]; new_leafnode->keys[slot] = leafnode->keys[slot]; leafnode->hashes[slot] = 0; leafnode->keys[slot].clear(); } } auto target = key.compare(split_key) > 0 ? new_leafnode.get() : leafnode; LeafFillEmptySlot(target, hash, key, value); }); // recursively update volatile parents outside persistent transaction InnerUpdateAfterSplit(leafnode, move(new_leafnode), &split_key); } void tree3::InnerUpdateAfterSplit(internal::tree3::KVNode *node, unique_ptr new_node, std::string *split_key) { if (!node->parent) { assert(node == tree_top.get()); LOG(" creating new top node for split_key=" << *split_key); unique_ptr top( new internal::tree3::KVInnerNode()); top->keycount = 1; top->keys[0] = *split_key; node->parent = top.get(); new_node->parent = top.get(); top->children[0] = move(tree_top); top->children[1] = move(new_node); #ifndef NDEBUG top->assert_invariants(); #endif tree_top = move(top); // assign new top node return; // end recursion } LOG(" updating parents for split_key=" << *split_key); internal::tree3::KVInnerNode *inner = node->parent; { // insert split_key and new_node into inner node in sorted order const uint8_t keycount = inner->keycount; int idx = 0; // position where split_key should be inserted while (idx < keycount && inner->keys[idx].compare(*split_key) <= 0) idx++; for (int i = keycount - 1; i >= idx; i--) inner->keys[i + 1] = move(inner->keys[i]); for (int i = keycount; i > idx; i--) inner->children[i + 1] = move(inner->children[i]); inner->keys[idx] = *split_key; inner->children[idx + 1] = move(new_node); inner->keycount = (uint8_t)(keycount + 1); } const uint8_t keycount = inner->keycount; if (keycount <= INNER_KEYS) { #ifndef NDEBUG inner->assert_invariants(); #endif return; // end recursion } // split inner node at the midpoint, update parents as needed unique_ptr ni( new internal::tree3::KVInnerNode()); // create new inner node ni->parent = inner->parent; // set parent reference for (int i = INNER_KEYS_UPPER; i < keycount; i++) { // move all upper keys ni->keys[i - INNER_KEYS_UPPER] = move(inner->keys[i]); // move key string } for (int i = INNER_KEYS_UPPER; i < keycount + 1; i++) { // move all upper children ni->children[i - INNER_KEYS_UPPER] = move(inner->children[i]); // move child reference ni->children[i - INNER_KEYS_UPPER]->parent = ni.get(); // set parent reference } ni->keycount = INNER_KEYS_MIDPOINT; // always half the keys std::string new_split_key = inner->keys[INNER_KEYS_MIDPOINT]; // save for recursion inner->keycount = INNER_KEYS_MIDPOINT; // half of keys remain // perform deep check on modified inner nodes #ifndef NDEBUG inner->assert_invariants(); // check node just split ni->assert_invariants(); // check new node #endif InnerUpdateAfterSplit(inner, move(ni), &new_split_key); // recursive update } // =============================================================================================== // PROTECTED LIFECYCLE METHODS // =============================================================================================== void tree3::Recover() { LOG("Recovering"); // traverse persistent leaves to build list of leaves to recover std::list leaves; auto root_leaf = persistent_ptr(*root_oid); while (root_leaf) { unique_ptr leafnode( new internal::tree3::KVLeafNode()); leafnode->leaf = root_leaf; leafnode->is_leaf = true; // find highest sorting key in leaf, while recovering all hashes bool empty_leaf = true; std::string max_key; for (int slot = LEAF_KEYS; slot--;) { auto kvslot = root_leaf->slots[slot].get_ro(); if (kvslot.empty()) continue; leafnode->hashes[slot] = kvslot.hash(); if (leafnode->hashes[slot] == 0) continue; const char *key = kvslot.key(); if (empty_leaf) { max_key = std::string(kvslot.key(), kvslot.get_ks()); empty_leaf = false; } else if (max_key.compare(0, std::string::npos, kvslot.key(), kvslot.get_ks()) < 0) { max_key = std::string(kvslot.key(), kvslot.get_ks()); } leafnode->keys[slot] = std::string(key, kvslot.get_ks()); } // use highest sorting key to decide how to recover the leaf if (empty_leaf) { leaves_prealloc.push_back(root_leaf); } else { leaves.push_back({move(leafnode), max_key}); } root_leaf = root_leaf->next.get(); // advance to next linked leaf } // sort recovered leaves in ascending key order leaves.sort([](const internal::tree3::KVRecoveredLeaf &lhs, const internal::tree3::KVRecoveredLeaf &rhs) { return (lhs.max_key.compare(rhs.max_key) < 0); }); // reconstruct top/inner nodes using adjacent pairs of recovered leaves tree_top.reset(nullptr); if (!leaves.empty()) { tree_top = move(leaves.front().leafnode); auto max_key = leaves.front().max_key; leaves.pop_front(); auto prevnode = tree_top.get(); while (!leaves.empty()) { std::string split_key = std::string(max_key); auto nextnode = leaves.front().leafnode.get(); nextnode->parent = prevnode->parent; InnerUpdateAfterSplit(prevnode, move(leaves.front().leafnode), &split_key); max_key = leaves.front().max_key; leaves.pop_front(); prevnode = nextnode; } } LOG("Recovered ok"); } // =============================================================================================== // PEARSON HASH METHODS // =============================================================================================== // Pearson hashing lookup table from RFC 3074 const uint8_t PEARSON_LOOKUP_TABLE[256] = { 251, 175, 119, 215, 81, 14, 79, 191, 103, 49, 181, 143, 186, 157, 0, 232, 31, 32, 55, 60, 152, 58, 17, 237, 174, 70, 160, 144, 220, 90, 57, 223, 59, 3, 18, 140, 111, 166, 203, 196, 134, 243, 124, 95, 222, 179, 197, 65, 180, 48, 36, 15, 107, 46, 233, 130, 165, 30, 123, 161, 209, 23, 97, 16, 40, 91, 219, 61, 100, 10, 210, 109, 250, 127, 22, 138, 29, 108, 244, 67, 207, 9, 178, 204, 74, 98, 126, 249, 167, 116, 34, 77, 193, 200, 121, 5, 20, 113, 71, 35, 128, 13, 182, 94, 25, 226, 227, 199, 75, 27, 41, 245, 230, 224, 43, 225, 177, 26, 155, 150, 212, 142, 218, 115, 241, 73, 88, 105, 39, 114, 62, 255, 192, 201, 145, 214, 168, 158, 221, 148, 154, 122, 12, 84, 82, 163, 44, 139, 228, 236, 205, 242, 217, 11, 187, 146, 159, 64, 86, 239, 195, 42, 106, 198, 118, 112, 184, 172, 87, 2, 173, 117, 176, 229, 247, 253, 137, 185, 99, 164, 102, 147, 45, 66, 231, 52, 141, 211, 194, 206, 246, 238, 56, 110, 78, 248, 63, 240, 189, 93, 92, 51, 53, 183, 19, 171, 72, 50, 33, 104, 101, 69, 8, 252, 83, 120, 76, 135, 85, 54, 202, 125, 188, 213, 96, 235, 136, 208, 162, 129, 190, 132, 156, 38, 47, 1, 7, 254, 24, 4, 216, 131, 89, 21, 28, 133, 37, 153, 149, 80, 170, 68, 6, 169, 234, 151}; // Modified Pearson hashing algorithm from RFC 3074 uint8_t tree3::PearsonHash(const char *data, const size_t size) { auto hash = (uint8_t)size; for (size_t i = size; i > 0;) { hash = PEARSON_LOOKUP_TABLE[hash ^ data[--i]]; } // MODIFICATION START return (hash == 0) ? (uint8_t)1 : hash; // 0 reserved for "null" // MODIFICATION END } // =============================================================================================== // SLOT CLASS METHODS // =============================================================================================== bool internal::tree3::KVSlot::empty() { if (kv) return false; else return true; } void internal::tree3::KVSlot::clear() { if (kv) { char *p = kv.get(); set_ph_direct(p, 0); set_ks_direct(p, 0); set_vs_direct(p, 0); delete_persistent(kv, sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + get_ks_direct(p) + get_vs_direct(p) + 2); kv = nullptr; } } void internal::tree3::KVSlot::set(const uint8_t hash, const std::string &key, const std::string &value) { if (kv) { char *p = kv.get(); delete_persistent(kv, sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + get_ks_direct(p) + get_vs_direct(p) + 2); } size_t ksize; size_t vsize; ksize = key.size(); vsize = value.size(); size_t size = ksize + vsize + 2 + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); kv = make_persistent(size); char *p = kv.get(); set_ph_direct(p, hash); set_ks_direct(p, (uint32_t)ksize); set_vs_direct(p, (uint32_t)vsize); char *kvptr = p + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); memcpy(kvptr, key.data(), ksize); // copy key into buffer kvptr += ksize + 1; // advance ptr past key memcpy(kvptr, value.data(), vsize); // copy value into buffer } // =============================================================================================== // Node invariants // =============================================================================================== void internal::tree3::KVInnerNode::assert_invariants() { assert(keycount <= INNER_KEYS); for (auto i = 0; i < keycount; ++i) { assert(keys[i].size() > 0); assert(children[i] != nullptr); } assert(children[keycount] != nullptr); for (auto i = keycount + 1; i < INNER_KEYS + 1; ++i) assert(children[i] == nullptr); } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines-experimental/tree3.h000066400000000000000000000171221361504041500207610ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../pmemobj_engine.h" #include #include #include #include #include #include #include using pmem::obj::delete_persistent; using pmem::obj::make_persistent; using pmem::obj::p; using pmem::obj::persistent_ptr; using pmem::obj::pool; using pmem::obj::transaction; using std::move; using std::unique_ptr; using std::vector; namespace pmem { namespace kv { namespace internal { namespace tree3 { #define INNER_KEYS 4 // maximum keys for inner nodes #define INNER_KEYS_MIDPOINT (INNER_KEYS / 2) // halfway point within the node #define INNER_KEYS_UPPER ((INNER_KEYS / 2) + 1) // index where upper half of keys begins #define LEAF_KEYS 48 // maximum keys in tree nodes #define LEAF_KEYS_MIDPOINT (LEAF_KEYS / 2) // halfway point within the node class KVSlot { public: uint8_t hash() const { return get_ph(); } static uint8_t hash_direct(char *p) { return *((uint8_t *)(p + sizeof(uint32_t) + sizeof(uint32_t))); } const char *key() const { return ((char *)(kv.get()) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t)); } static const char *key_direct(char *p) { return (p + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t)); } const uint32_t keysize() const { return get_ks(); } static const uint32_t keysize_direct(char *p) { return *((uint32_t *)(p)); } const char *val() const { return ((char *)(kv.get()) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + get_ks() + 1); } static const char *val_direct(char *p) { return (p + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + *((uint32_t *)(p)) + 1); } const uint32_t valsize() const { return get_vs(); } static const uint32_t valsize_direct(char *p) { return *((uint32_t *)(p + sizeof(uint32_t))); } void clear(); void set(const uint8_t hash, const std::string &key, const std::string &value); void set_ph(uint8_t v) { *((uint8_t *)((char *)(kv.get()) + sizeof(uint32_t) + sizeof(uint32_t))) = v; } static void set_ph_direct(char *p, uint8_t v) { *((uint8_t *)(p + sizeof(uint32_t) + sizeof(uint32_t))) = v; } void set_ks(uint32_t v) { *((uint32_t *)(kv.get())) = v; } static void set_ks_direct(char *p, uint32_t v) { *((uint32_t *)(p)) = v; } void set_vs(uint32_t v) { *((uint32_t *)((char *)(kv.get()) + sizeof(uint32_t))) = v; } static void set_vs_direct(char *p, uint32_t v) { *((uint32_t *)((char *)(p) + sizeof(uint32_t))) = v; } uint8_t get_ph() const { return *((uint8_t *)((char *)(kv.get()) + sizeof(uint32_t) + sizeof(uint32_t))); } static uint8_t get_ph_direct(char *p) { return *((uint8_t *)((char *)(p) + sizeof(uint32_t) + sizeof(uint32_t))); } uint32_t get_ks() const { return *((uint32_t *)(kv.get())); } static uint32_t get_ks_direct(char *p) { return *((uint32_t *)(p)); } uint32_t get_vs() const { return *((uint32_t *)((char *)(kv.get()) + sizeof(uint32_t))); } static uint32_t get_vs_direct(char *p) { return *((uint32_t *)((char *)(p) + sizeof(uint32_t))); } bool empty(); private: persistent_ptr kv; // buffer for key & value }; struct KVLeaf { p slots[LEAF_KEYS]; // array of slot containers persistent_ptr next; // next leaf in unsorted list }; struct KVInnerNode; struct KVNode { // volatile nodes of the tree bool is_leaf = false; // indicate inner or leaf node KVInnerNode *parent; // parent of this node (null if top) virtual ~KVNode() = default; }; struct KVInnerNode final : KVNode { // volatile inner nodes of the tree uint8_t keycount; // count of keys in this node std::string keys[INNER_KEYS + 1]; // child keys plus one overflow slot unique_ptr children[INNER_KEYS + 2]; // child nodes plus one overflow slot void assert_invariants(); }; struct KVLeafNode final : KVNode { // volatile leaf nodes of the tree uint8_t hashes[LEAF_KEYS]; // Pearson hashes of keys std::string keys[LEAF_KEYS]; // keys stored in this leaf persistent_ptr leaf; // pointer to persistent leaf }; struct KVRecoveredLeaf { // temporary wrapper used for recovery unique_ptr leafnode; // leaf node being recovered std::string max_key; // highest sorting key present }; } /* namespace tree3 */ } /* namespace internal */ class tree3 : public pmemobj_engine_base { // hybrid B+ tree engine public: tree3(std::unique_ptr cfg); ~tree3(); std::string name() final; status count_all(std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; protected: internal::tree3::KVLeafNode *LeafSearch(const std::string &key); void LeafFillEmptySlot(internal::tree3::KVLeafNode *leafnode, uint8_t hash, const std::string &key, const std::string &value); bool LeafFillSlotForKey(internal::tree3::KVLeafNode *leafnode, uint8_t hash, const std::string &key, const std::string &value); void LeafFillSpecificSlot(internal::tree3::KVLeafNode *leafnode, uint8_t hash, const std::string &key, const std::string &value, int slot); void LeafSplitFull(internal::tree3::KVLeafNode *leafnode, uint8_t hash, const std::string &key, const std::string &value); void InnerUpdateAfterSplit(internal::tree3::KVNode *node, unique_ptr newnode, std::string *split_key); uint8_t PearsonHash(const char *data, size_t size); void Recover(); private: tree3(const tree3 &); // prevent copying void operator=(const tree3 &); // prevent assigning vector> leaves_prealloc; // persisted but unused leaves unique_ptr tree_top; // pointer to uppermost inner node }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines/000077500000000000000000000000001361504041500150705ustar00rootroot00000000000000pmemkv-1.1/src/engines/blackhole.cc000066400000000000000000000110401361504041500173170ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "blackhole.h" #include namespace pmem { namespace kv { blackhole::blackhole(std::unique_ptr cfg) { LOG("Started ok"); } blackhole::~blackhole() { LOG("Stopped ok"); } std::string blackhole::name() { return "blackhole"; } status blackhole::count_all(std::size_t &cnt) { LOG("count_all"); cnt = 0; return status::OK; } status blackhole::count_above(string_view key, std::size_t &cnt) { LOG("count_above for key=" << std::string(key.data(), key.size())); cnt = 0; return status::OK; } status blackhole::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_equal_above for key=" << std::string(key.data(), key.size())); cnt = 0; return status::OK; } status blackhole::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_equal_below for key=" << std::string(key.data(), key.size())); cnt = 0; return status::OK; } status blackhole::count_below(string_view key, std::size_t &cnt) { LOG("count_below for key=" << std::string(key.data(), key.size())); cnt = 0; return status::OK; } status blackhole::count_between(string_view key1, string_view key2, std::size_t &cnt) { LOG("count_between for key1=" << key1.data() << ", key2=" << key2.data()); cnt = 0; return status::OK; } status blackhole::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); return status::NOT_FOUND; } status blackhole::get_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_above for key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above for key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_below for key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::get_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_below for key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { LOG("get_between for key1=" << key1.data() << ", key2=" << key2.data()); return status::NOT_FOUND; } status blackhole::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); return status::NOT_FOUND; } status blackhole::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); return status::OK; } status blackhole::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); return status::OK; } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines/blackhole.h000066400000000000000000000056441361504041500171760ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../engine.h" namespace pmem { namespace kv { class blackhole : public engine_base { public: blackhole(std::unique_ptr cfg); ~blackhole(); std::string name() final; status count_all(std::size_t &cnt) final; status count_above(string_view key, std::size_t &cnt) final; status count_equal_above(string_view key, std::size_t &cnt) final; status count_equal_below(string_view key, std::size_t &cnt) final; status count_below(string_view key, std::size_t &cnt) final; status count_between(string_view key1, string_view key2, std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status get_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_below(string_view key, get_kv_callback *callback, void *arg) final; status get_below(string_view key, get_kv_callback *callback, void *arg) final; status get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines/cmap.cc000066400000000000000000000110031361504041500163120ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "cmap.h" #include "../out.h" #include namespace pmem { namespace kv { cmap::cmap(std::unique_ptr cfg) : pmemobj_engine_base(cfg) { static_assert( sizeof(internal::cmap::string_t) == 40, "Wrong size of cmap value and key. This probably means that std::string has size > 32"); LOG("Started ok"); Recover(); } cmap::~cmap() { LOG("Stopped ok"); } std::string cmap::name() { return "cmap"; } status cmap::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); cnt = container->size(); return status::OK; } status cmap::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); for (auto it = container->begin(); it != container->end(); ++it) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status cmap::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); return container->count(key) == 1 ? status::OK : status::NOT_FOUND; } status cmap::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); check_outside_tx(); internal::cmap::map_t::const_accessor result; bool found = container->find(result, key); if (!found) { LOG(" key not found"); return status::NOT_FOUND; } callback(result->second.c_str(), result->second.size(), arg); return status::OK; } status cmap::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); check_outside_tx(); container->insert_or_assign(key, value); return status::OK; } status cmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); bool erased = container->erase(key); return erased ? status::OK : status::NOT_FOUND; } status cmap::defrag(double start_percent, double amount_percent) { LOG("defrag: start_percent = " << start_percent << " amount_percent = " << amount_percent); check_outside_tx(); try { container->defragment(start_percent, amount_percent); } catch (std::range_error &e) { out_err_stream("defrag") << e.what(); return status::INVALID_ARGUMENT; } catch (pmem::defrag_error &e) { out_err_stream("defrag") << e.what(); return status::DEFRAG_ERROR; } return status::OK; } void cmap::Recover() { if (!OID_IS_NULL(*root_oid)) { container = (pmem::kv::internal::cmap::map_t *)pmemobj_direct(*root_oid); container->runtime_initialize(); } else { pmem::obj::transaction::run(pmpool, [&] { pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent().raw(); container = (pmem::kv::internal::cmap::map_t *)pmemobj_direct( *root_oid); container->runtime_initialize(); }); } } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines/cmap.h000066400000000000000000000066401361504041500161670ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../pmemobj_engine.h" #include "../polymorphic_string.h" #include #include namespace pmem { namespace kv { namespace internal { namespace cmap { class key_equal { public: template bool operator()(const M &lhs, const U &rhs) const { return lhs == rhs; } }; class string_hasher { /* hash multiplier used by fibonacci hashing */ static const size_t hash_multiplier = 11400714819323198485ULL; public: using transparent_key_equal = key_equal; size_t operator()(const pmem::kv::polymorphic_string &str) const { return hash(str.c_str(), str.size()); } size_t operator()(string_view str) const { return hash(str.data(), str.size()); } private: size_t hash(const char *str, size_t size) const { size_t h = 0; for (size_t i = 0; i < size; ++i) { h = static_cast(str[i]) ^ (h * hash_multiplier); } return h; } }; using string_t = pmem::kv::polymorphic_string; using map_t = pmem::obj::concurrent_hash_map; } /* namespace cmap */ } /* namespace internal */ class cmap : public pmemobj_engine_base { public: cmap(std::unique_ptr cfg); ~cmap(); cmap(const cmap &) = delete; cmap &operator=(const cmap &) = delete; std::string name() final; status count_all(std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; status defrag(double start_percent, double amount_percent) final; private: void Recover(); internal::cmap::map_t *container; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines/vcmap.cc000066400000000000000000000111551361504041500165100ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "vcmap.h" #include "../out.h" #include #include namespace pmem { namespace kv { static std::string get_path(internal::config &cfg) { const char *path; if (!cfg.get_string("path", &path)) throw internal::invalid_argument( "Config does not contain item with key: \"path\""); return std::string(path); } static uint64_t get_size(internal::config &cfg) { std::size_t size; if (!cfg.get_uint64("size", &size)) throw internal::invalid_argument( "Config does not contain item with key: \"size\""); return size; } vcmap::vcmap(std::unique_ptr cfg) : kv_allocator(get_path(*cfg), get_size(*cfg)), ch_allocator(kv_allocator), pmem_kv_container(std::scoped_allocator_adaptor(kv_allocator)) { LOG("Started ok"); } vcmap::~vcmap() { LOG("Stopped ok"); } std::string vcmap::name() { return "vcmap"; } status vcmap::count_all(std::size_t &cnt) { LOG("count_all"); cnt = pmem_kv_container.size(); return status::OK; } status vcmap::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); for (auto &iterator : pmem_kv_container) { auto ret = callback(iterator.first.c_str(), iterator.first.size(), iterator.second.c_str(), iterator.second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vcmap::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); map_t::const_accessor result; // XXX - do not create temporary string const bool result_found = pmem_kv_container.find( result, pmem_string(key.data(), key.size(), ch_allocator)); return (result_found ? status::OK : status::NOT_FOUND); } status vcmap::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); map_t::const_accessor result; // XXX - do not create temporary string const bool result_found = pmem_kv_container.find( result, pmem_string(key.data(), key.size(), ch_allocator)); if (!result_found) { LOG(" key not found"); return status::NOT_FOUND; } callback(result->second.c_str(), result->second.size(), arg); return status::OK; } status vcmap::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); map_t::value_type kv_pair{// XXX - do not create temporary string pmem_string(key.data(), key.size(), ch_allocator), pmem_string(value.data(), value.size(), ch_allocator)}; bool result = pmem_kv_container.insert(kv_pair); if (!result) { map_t::accessor result_found; pmem_kv_container.find(result_found, kv_pair.first); result_found->second = kv_pair.second; } return status::OK; } status vcmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string size_t erased = pmem_kv_container.erase( pmem_string(key.data(), key.size(), ch_allocator)); return (erased == 1) ? status::OK : status::NOT_FOUND; } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines/vcmap.h000066400000000000000000000054761361504041500163630ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../engine.h" #include "pmem_allocator.h" #include #include #include #ifdef USE_LIBMEMKIND_NAMESPACE namespace memkind_ns = libmemkind::pmem; #else namespace memkind_ns = pmem; #endif namespace pmem { namespace kv { class vcmap : public engine_base { public: vcmap(std::unique_ptr cfg); ~vcmap(); std::string name() final; status count_all(std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; private: typedef memkind_ns::allocator ch_allocator_t; typedef std::basic_string, ch_allocator_t> pmem_string; typedef memkind_ns::allocator> kv_allocator_t; typedef tbb::concurrent_hash_map, std::scoped_allocator_adaptor> map_t; kv_allocator_t kv_allocator; ch_allocator_t ch_allocator; map_t pmem_kv_container; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/engines/vsmap.cc000066400000000000000000000221241361504041500165260ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "vsmap.h" #include "../out.h" #include #include namespace pmem { namespace kv { static std::string get_path(internal::config &cfg) { const char *path; if (!cfg.get_string("path", &path)) throw internal::invalid_argument( "Config does not contain item with key: \"path\""); return std::string(path); } static uint64_t get_size(internal::config &cfg) { std::size_t size; if (!cfg.get_uint64("size", &size)) throw internal::invalid_argument( "Config does not contain item with key: \"size\""); return size; } vsmap::vsmap(std::unique_ptr cfg) : kv_allocator(get_path(*cfg), get_size(*cfg)), pmem_kv_container(kv_allocator) { LOG("Started ok"); } vsmap::~vsmap() { LOG("Stopped ok"); } std::string vsmap::name() { return "vsmap"; } status vsmap::count_all(std::size_t &cnt) { cnt = pmem_kv_container.size(); return status::OK; } status vsmap::count_above(string_view key, std::size_t &cnt) { LOG("count_above for key=" << std::string(key.data(), key.size())); std::size_t result = 0; // XXX - do not create temporary string auto it = pmem_kv_container.upper_bound( key_type(key.data(), key.size(), kv_allocator)); auto end = pmem_kv_container.end(); for (; it != end; it++) result++; cnt = result; return status::OK; } status vsmap::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_equal_above for key=" << std::string(key.data(), key.size())); std::size_t result = 0; // XXX - do not create temporary string auto it = pmem_kv_container.lower_bound( key_type(key.data(), key.size(), kv_allocator)); auto end = pmem_kv_container.end(); for (; it != end; it++) result++; cnt = result; return status::OK; } status vsmap::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_equal_below for key=" << std::string(key.data(), key.size())); std::size_t result = 0; auto it = pmem_kv_container.begin(); // XXX - do not create temporary string auto end = pmem_kv_container.upper_bound( key_type(key.data(), key.size(), kv_allocator)); for (; it != end; it++) result++; cnt = result; return status::OK; } status vsmap::count_below(string_view key, std::size_t &cnt) { LOG("count_below for key=" << std::string(key.data(), key.size())); std::size_t result = 0; auto it = pmem_kv_container.begin(); // XXX - do not create temporary string auto end = pmem_kv_container.lower_bound( key_type(key.data(), key.size(), kv_allocator)); for (; it != end; it++) result++; cnt = result; return status::OK; } status vsmap::count_between(string_view key1, string_view key2, std::size_t &cnt) { LOG("count_between for key1=" << key1.data() << ", key2=" << key2.data()); std::size_t result = 0; if (key1.compare(key2) < 0) { // XXX - do not create temporary string auto it = pmem_kv_container.upper_bound( key_type(key1.data(), key1.size(), kv_allocator)); auto end = pmem_kv_container.lower_bound( key_type(key2.data(), key2.size(), kv_allocator)); for (; it != end; it++) result++; } cnt = result; return status::OK; } status vsmap::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); for (auto &it : pmem_kv_container) { auto ret = callback(it.first.c_str(), it.first.size(), it.second.c_str(), it.second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vsmap::get_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_above for key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string auto it = pmem_kv_container.upper_bound( key_type(key.data(), key.size(), kv_allocator)); auto end = pmem_kv_container.end(); for (; it != end; it++) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vsmap::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above for key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string auto it = pmem_kv_container.lower_bound( key_type(key.data(), key.size(), kv_allocator)); auto end = pmem_kv_container.end(); for (; it != end; it++) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vsmap::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above for key=" << std::string(key.data(), key.size())); auto it = pmem_kv_container.begin(); // XXX - do not create temporary string auto end = pmem_kv_container.upper_bound( key_type(key.data(), key.size(), kv_allocator)); for (; it != end; it++) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vsmap::get_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_below for key=" << std::string(key.data(), key.size())); auto it = pmem_kv_container.begin(); // XXX - do not create temporary string auto end = pmem_kv_container.lower_bound( key_type(key.data(), key.size(), kv_allocator)); for (; it != end; it++) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status vsmap::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { LOG("get_between for key1=" << key1.data() << ", key2=" << key2.data()); if (key1.compare(key2) < 0) { // XXX - do not create temporary string auto it = pmem_kv_container.upper_bound( key_type(key1.data(), key1.size(), kv_allocator)); auto end = pmem_kv_container.lower_bound( key_type(key2.data(), key2.size(), kv_allocator)); for (; it != end; it++) { auto ret = callback(it->first.c_str(), it->first.size(), it->second.c_str(), it->second.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } } return status::OK; } status vsmap::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string bool r = pmem_kv_container.find(key_type(key.data(), key.size(), kv_allocator)) != pmem_kv_container.end(); return (r ? status::OK : status::NOT_FOUND); } status vsmap::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string const auto pos = pmem_kv_container.find(key_type(key.data(), key.size(), kv_allocator)); if (pos == pmem_kv_container.end()) { LOG(" key not found"); return status::NOT_FOUND; } callback(pos->second.c_str(), pos->second.size(), arg); return status::OK; } status vsmap::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); // XXX - do not create temporary string pmem_kv_container[key_type(key.data(), key.size(), kv_allocator)] = mapped_type(value.data(), value.size(), kv_allocator); return status::OK; } status vsmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string size_t erased = pmem_kv_container.erase(key_type(key.data(), key.size(), kv_allocator)); return (erased == 1) ? status::OK : status::NOT_FOUND; } } // namespace kv } // namespace pmem pmemkv-1.1/src/engines/vsmap.h000066400000000000000000000071011361504041500163660ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include "../engine.h" #include "pmem_allocator.h" #include #include #include #ifdef USE_LIBMEMKIND_NAMESPACE namespace memkind_ns = libmemkind::pmem; #else namespace memkind_ns = pmem; #endif namespace pmem { namespace kv { class vsmap : public engine_base { public: vsmap(std::unique_ptr cfg); ~vsmap(); std::string name() final; status count_all(std::size_t &cnt) final; status count_above(string_view key, std::size_t &cnt) final; status count_equal_above(string_view key, std::size_t &cnt) final; status count_equal_below(string_view key, std::size_t &cnt) final; status count_below(string_view key, std::size_t &cnt) final; status count_between(string_view key1, string_view key2, std::size_t &cnt) final; status get_all(get_kv_callback *callback, void *arg) final; status get_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_above(string_view key, get_kv_callback *callback, void *arg) final; status get_equal_below(string_view key, get_kv_callback *callback, void *arg) final; status get_below(string_view key, get_kv_callback *callback, void *arg) final; status get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) final; status exists(string_view key) final; status get(string_view key, get_v_callback *callback, void *arg) final; status put(string_view key, string_view value) final; status remove(string_view key) final; private: using storage_type = std::basic_string, memkind_ns::allocator>; using key_type = storage_type; using mapped_type = storage_type; using map_allocator_type = memkind_ns::allocator>; using map_type = std::map, std::scoped_allocator_adaptor>; map_allocator_type kv_allocator; map_type pmem_kv_container; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/exceptions.h000066400000000000000000000052771361504041500160050ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_EXCEPTIONS_H #define LIBPMEMKV_EXCEPTIONS_H #include #include "libpmemkv.h" namespace pmem { namespace kv { namespace internal { struct error : std::runtime_error { error(const std::string &msg, int status_code = PMEMKV_STATUS_UNKNOWN_ERROR) : std::runtime_error(msg), status_code(status_code) { } int status_code; }; struct not_supported : error { not_supported(const std::string &msg) : error(msg, PMEMKV_STATUS_NOT_SUPPORTED) { } }; struct invalid_argument : error { invalid_argument(const std::string &msg) : error(msg, PMEMKV_STATUS_INVALID_ARGUMENT) { } }; struct config_parsing_error : error { config_parsing_error(const std::string &msg) : error(msg, PMEMKV_STATUS_CONFIG_PARSING_ERROR) { } }; struct config_type_error : error { config_type_error(const std::string &msg) : error(msg, PMEMKV_STATUS_CONFIG_TYPE_ERROR) { } }; struct wrong_engine_name : error { wrong_engine_name(const std::string &msg) : error(msg, PMEMKV_STATUS_WRONG_ENGINE_NAME) { } }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_EXCEPTIONS_H */ pmemkv-1.1/src/libpmemkv.cc000066400000000000000000000320501361504041500157350ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include "config.h" #include "engine.h" #include "exceptions.h" #include "libpmemkv.h" #include "libpmemkv.hpp" #include "libpmemobj++/pexceptions.hpp" #include "out.h" #include #include #include #include #include static inline pmemkv_config *config_from_internal(pmem::kv::internal::config *config) { return reinterpret_cast(config); } static inline pmem::kv::internal::config *config_to_internal(pmemkv_config *config) { return reinterpret_cast(config); } static inline pmem::kv::engine_base *db_to_internal(pmemkv_db *db) { return reinterpret_cast(db); } static inline pmemkv_db *db_from_internal(pmem::kv::engine_base *db) { return reinterpret_cast(db); } template static inline int catch_and_return_status(const char *func_name, Function &&f) { try { return static_cast(f()); } catch (pmem::kv::internal::error &e) { out_err_stream(func_name) << e.what(); return e.status_code; } catch (std::bad_alloc &e) { out_err_stream(func_name) << e.what(); return PMEMKV_STATUS_OUT_OF_MEMORY; } catch (std::runtime_error &e) { out_err_stream(func_name) << e.what(); return PMEMKV_STATUS_UNKNOWN_ERROR; } catch (pmem::transaction_scope_error &e) { out_err_stream(func_name) << e.what(); return PMEMKV_STATUS_TRANSACTION_SCOPE_ERROR; } catch (...) { out_err_stream(func_name) << "Unspecified error"; return PMEMKV_STATUS_UNKNOWN_ERROR; } } extern "C" { pmemkv_config *pmemkv_config_new(void) { try { return config_from_internal(new pmem::kv::internal::config); } catch (const std::exception &exc) { ERR() << exc.what(); return nullptr; } catch (...) { ERR() << "Unspecified failure"; return nullptr; } } void pmemkv_config_delete(pmemkv_config *config) { try { delete config_to_internal(config); } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } int pmemkv_config_put_data(pmemkv_config *config, const char *key, const void *value, size_t value_size) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_data(key, value, value_size); return PMEMKV_STATUS_OK; }); } int pmemkv_config_put_object(pmemkv_config *config, const char *key, void *value, void (*deleter)(void *)) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_object(key, value, deleter); return PMEMKV_STATUS_OK; }); } int pmemkv_config_put_int64(pmemkv_config *config, const char *key, int64_t value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_int64(key, value); return PMEMKV_STATUS_OK; }); } int pmemkv_config_put_uint64(pmemkv_config *config, const char *key, uint64_t value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_uint64(key, value); return PMEMKV_STATUS_OK; }); } int pmemkv_config_put_string(pmemkv_config *config, const char *key, const char *value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_string(key, value); return PMEMKV_STATUS_OK; }); } int pmemkv_config_get_data(pmemkv_config *config, const char *key, const void **value, size_t *value_size) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return config_to_internal(config)->get_data(key, value, value_size) ? PMEMKV_STATUS_OK : PMEMKV_STATUS_NOT_FOUND; }); } int pmemkv_config_get_object(pmemkv_config *config, const char *key, void **value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return config_to_internal(config)->get_object(key, value) ? PMEMKV_STATUS_OK : PMEMKV_STATUS_NOT_FOUND; }); } int pmemkv_config_get_int64(pmemkv_config *config, const char *key, int64_t *value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return config_to_internal(config)->get_int64(key, value) ? PMEMKV_STATUS_OK : PMEMKV_STATUS_NOT_FOUND; }); } int pmemkv_config_get_uint64(pmemkv_config *config, const char *key, uint64_t *value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return config_to_internal(config)->get_uint64(key, value) ? PMEMKV_STATUS_OK : PMEMKV_STATUS_NOT_FOUND; }); } int pmemkv_config_get_string(pmemkv_config *config, const char *key, const char **value) { if (!config) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return config_to_internal(config)->get_string(key, value) ? PMEMKV_STATUS_OK : PMEMKV_STATUS_NOT_FOUND; }); } int pmemkv_open(const char *engine_c_str, pmemkv_config *config, pmemkv_db **db) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { std::unique_ptr cfg( config_to_internal(config)); auto engine = pmem::kv::engine_base::create_engine(engine_c_str, std::move(cfg)); *db = db_from_internal(engine.release()); return PMEMKV_STATUS_OK; }); } void pmemkv_close(pmemkv_db *db) { try { delete db_to_internal(db); } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } int pmemkv_count_all(pmemkv_db *db, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status( __func__, [&] { return db_to_internal(db)->count_all(*cnt); }); } int pmemkv_count_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->count_above(pmem::kv::string_view(k, kb), *cnt); }); } int pmemkv_count_equal_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->count_equal_above(pmem::kv::string_view(k, kb), *cnt); }); } int pmemkv_count_equal_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->count_equal_below(pmem::kv::string_view(k, kb), *cnt); }); } int pmemkv_count_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->count_below(pmem::kv::string_view(k, kb), *cnt); }); } int pmemkv_count_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, size_t *cnt) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->count_between(pmem::kv::string_view(k1, kb1), pmem::kv::string_view(k2, kb2), *cnt); }); } int pmemkv_get_all(pmemkv_db *db, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status( __func__, [&] { return db_to_internal(db)->get_all(c, arg); }); } int pmemkv_get_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get_above(pmem::kv::string_view(k, kb), c, arg); }); } int pmemkv_get_equal_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get_equal_above(pmem::kv::string_view(k, kb), c, arg); }); } int pmemkv_get_equal_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get_equal_below(pmem::kv::string_view(k, kb), c, arg); }); } int pmemkv_get_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get_below(pmem::kv::string_view(k, kb), c, arg); }); } int pmemkv_get_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, pmemkv_get_kv_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get_between(pmem::kv::string_view(k1, kb1), pmem::kv::string_view(k2, kb2), c, arg); }); } int pmemkv_exists(pmemkv_db *db, const char *k, size_t kb) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->exists(pmem::kv::string_view(k, kb)); }); } int pmemkv_get(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_v_callback *c, void *arg) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->get(pmem::kv::string_view(k, kb), c, arg); }); } struct GetCopyCallbackContext { int result; size_t buffer_size; char *buffer; size_t *value_size; }; static void get_copy_callback(const char *v, size_t vb, void *arg) { const auto c = ((GetCopyCallbackContext *)arg); if (c->value_size != nullptr) *(c->value_size) = vb; if (vb <= c->buffer_size) { c->result = PMEMKV_STATUS_OK; if (c->buffer != nullptr) memcpy(c->buffer, v, vb); } else { c->result = PMEMKV_STATUS_OUT_OF_MEMORY; } } int pmemkv_get_copy(pmemkv_db *db, const char *k, size_t kb, char *buffer, size_t buffer_size, size_t *value_size) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; GetCopyCallbackContext ctx = {PMEMKV_STATUS_NOT_FOUND, buffer_size, buffer, value_size}; if (buffer != nullptr) memset(buffer, 0, buffer_size); auto ret = catch_and_return_status(__func__, [&] { return db_to_internal(db)->get(pmem::kv::string_view(k, kb), &get_copy_callback, &ctx); }); if (ret != PMEMKV_STATUS_OK) return ret; return ctx.result; } int pmemkv_put(pmemkv_db *db, const char *k, size_t kb, const char *v, size_t vb) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->put(pmem::kv::string_view(k, kb), pmem::kv::string_view(v, vb)); }); } int pmemkv_remove(pmemkv_db *db, const char *k, size_t kb) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->remove(pmem::kv::string_view(k, kb)); }); } int pmemkv_defrag(pmemkv_db *db, double start_percent, double amount_percent) { if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return db_to_internal(db)->defrag(start_percent, amount_percent); }); } const char *pmemkv_errormsg(void) { return out_get_errormsg(); } } /* extern "C" */ pmemkv-1.1/src/libpmemkv.h000066400000000000000000000124351361504041500156040ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_H #define LIBPMEMKV_H #include #include #ifdef __cplusplus extern "C" { #endif #define PMEMKV_STATUS_OK 0 #define PMEMKV_STATUS_UNKNOWN_ERROR 1 #define PMEMKV_STATUS_NOT_FOUND 2 #define PMEMKV_STATUS_NOT_SUPPORTED 3 #define PMEMKV_STATUS_INVALID_ARGUMENT 4 #define PMEMKV_STATUS_CONFIG_PARSING_ERROR 5 #define PMEMKV_STATUS_CONFIG_TYPE_ERROR 6 #define PMEMKV_STATUS_STOPPED_BY_CB 7 #define PMEMKV_STATUS_OUT_OF_MEMORY 8 #define PMEMKV_STATUS_WRONG_ENGINE_NAME 9 #define PMEMKV_STATUS_TRANSACTION_SCOPE_ERROR 10 #define PMEMKV_STATUS_DEFRAG_ERROR 11 typedef struct pmemkv_db pmemkv_db; typedef struct pmemkv_config pmemkv_config; typedef int pmemkv_get_kv_callback(const char *key, size_t keybytes, const char *value, size_t valuebytes, void *arg); typedef void pmemkv_get_v_callback(const char *value, size_t valuebytes, void *arg); pmemkv_config *pmemkv_config_new(void); void pmemkv_config_delete(pmemkv_config *config); int pmemkv_config_put_data(pmemkv_config *config, const char *key, const void *value, size_t value_size); int pmemkv_config_put_object(pmemkv_config *config, const char *key, void *value, void (*deleter)(void *)); int pmemkv_config_put_uint64(pmemkv_config *config, const char *key, uint64_t value); int pmemkv_config_put_int64(pmemkv_config *config, const char *key, int64_t value); int pmemkv_config_put_string(pmemkv_config *config, const char *key, const char *value); int pmemkv_config_get_data(pmemkv_config *config, const char *key, const void **value, size_t *value_size); int pmemkv_config_get_object(pmemkv_config *config, const char *key, void **value); int pmemkv_config_get_uint64(pmemkv_config *config, const char *key, uint64_t *value); int pmemkv_config_get_int64(pmemkv_config *config, const char *key, int64_t *value); int pmemkv_config_get_string(pmemkv_config *config, const char *key, const char **value); int pmemkv_open(const char *engine, pmemkv_config *config, pmemkv_db **db); void pmemkv_close(pmemkv_db *kv); int pmemkv_count_all(pmemkv_db *db, size_t *cnt); int pmemkv_count_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_equal_above(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_equal_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_below(pmemkv_db *db, const char *k, size_t kb, size_t *cnt); int pmemkv_count_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, size_t *cnt); int pmemkv_get_all(pmemkv_db *db, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_equal_above(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_equal_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_below(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_kv_callback *c, void *arg); int pmemkv_get_between(pmemkv_db *db, const char *k1, size_t kb1, const char *k2, size_t kb2, pmemkv_get_kv_callback *c, void *arg); int pmemkv_exists(pmemkv_db *db, const char *k, size_t kb); int pmemkv_get(pmemkv_db *db, const char *k, size_t kb, pmemkv_get_v_callback *c, void *arg); int pmemkv_get_copy(pmemkv_db *db, const char *k, size_t kb, char *buffer, size_t buffer_size, size_t *value_size); int pmemkv_put(pmemkv_db *db, const char *k, size_t kb, const char *v, size_t vb); int pmemkv_remove(pmemkv_db *db, const char *k, size_t kb); int pmemkv_defrag(pmemkv_db *db, double start_percent, double amount_percent); const char *pmemkv_errormsg(void); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* LIBPMEMKV_H */ pmemkv-1.1/src/libpmemkv.hpp000066400000000000000000001137341361504041500161500ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_HPP #define LIBPMEMKV_HPP #include #include #include #include #if __cpp_lib_string_view #include #endif #include "libpmemkv.h" /*! \file libpmemkv.hpp \brief Main C++ pmemkv public header. It contains all pmemkv public types, enums, classes with their functions and members. */ /*! \namespace pmem \brief Persistent memory namespace. It is a common namespace for all persistent memory C++ libraries For more information about pmem goto: https://pmem.io */ namespace pmem { /*! \namespace pmem::kv \brief Main pmemkv namespace. It contains all pmemkv public types, enums, classes with their functions and members. It is located within pmem namespace. */ namespace kv { #if __cpp_lib_string_view using string_view = std::string_view; #else /*! \class string_view \brief Our brief std::string_view implementation. If C++17's std::string_view implementation is not available, this one is used to avoid unnecessary string copying. */ class string_view { public: string_view() noexcept; string_view(const char *data, size_t size); string_view(const std::string &s); string_view(const char *data); string_view(const string_view &rhs) noexcept = default; string_view &operator=(const string_view &rhs) noexcept = default; const char *data() const noexcept; std::size_t size() const noexcept; int compare(const string_view &other) noexcept; private: const char *_data; std::size_t _size; }; #endif /** * The C++ idiomatic function type to use for callback using key-value pair. * * @param[in] key returned by callback item's key * @param[in] value returned by callback item's data */ typedef int get_kv_function(string_view key, string_view value); /** * The C++ idiomatic function type to use for callback using only the value. * It is used only by non-range get() calls. * * @param[in] value returned by callback item's data */ typedef void get_v_function(string_view value); /** * Key-value pair callback, C-style. */ using get_kv_callback = pmemkv_get_kv_callback; /** * Value-only callback, C-style. */ using get_v_callback = pmemkv_get_v_callback; /*! \enum status \brief Status returned by pmemkv functions. Each function, except for db::close() and pmem::kv::errormsg(), returns one of the following status codes. */ enum class status { OK = PMEMKV_STATUS_OK, /**< no error */ UNKNOWN_ERROR = PMEMKV_STATUS_UNKNOWN_ERROR, /**< unknown error */ NOT_FOUND = PMEMKV_STATUS_NOT_FOUND, /**< record (or config item) not found */ NOT_SUPPORTED = PMEMKV_STATUS_NOT_SUPPORTED, /**< function is not implemented by current engine */ INVALID_ARGUMENT = PMEMKV_STATUS_INVALID_ARGUMENT, /**< argument to function has wrong value */ CONFIG_PARSING_ERROR = PMEMKV_STATUS_CONFIG_PARSING_ERROR, /**< parsing data to config failed */ CONFIG_TYPE_ERROR = PMEMKV_STATUS_CONFIG_TYPE_ERROR, /**< config item has different type than expected */ STOPPED_BY_CB = PMEMKV_STATUS_STOPPED_BY_CB, /**< iteration was stopped by user's callback */ OUT_OF_MEMORY = PMEMKV_STATUS_OUT_OF_MEMORY, /**< operation failed because there is not enough memory (or space on the device) */ WRONG_ENGINE_NAME = PMEMKV_STATUS_WRONG_ENGINE_NAME, /**< engine name does not match any available engine */ TRANSACTION_SCOPE_ERROR = PMEMKV_STATUS_TRANSACTION_SCOPE_ERROR, /**< an error with the scope of the libpmemobj transaction */ DEFRAG_ERROR = PMEMKV_STATUS_DEFRAG_ERROR, /**< the defragmentation process failed (possibly in the middle of a run) */ }; /*! \class config \brief Holds configuration parameters for engines. It stores mappings of keys (strings) to values. A value can be: * uint64_t, * int64_t, * string, * binary data, * pointer to an object (with accompanying deleter function). It also delivers methods to store and read configuration items provided by a user. Once the configuration object is set (with all required parameters),pmemkv_open it can be passed to db::open() method. List of options which are required by pmemkv database is specific to an engine. Every engine has documented all supported config parameters (please see [libpmemkv(7)](https://pmem.io/pmemkv/master/manpages/libpmemkv.7.html) for details). */ class config { public: config() noexcept; explicit config(pmemkv_config *cfg) noexcept; ~config(); config(const config &other) = delete; config(config &&other) noexcept; config &operator=(const config &other) = delete; config &operator=(config &&other) noexcept; template status put_data(const std::string &key, const T *value, const std::size_t number = 1) noexcept; template status put_object( const std::string &key, T *value, void (*deleter)(void *) = [](T *value) { delete value; }) noexcept; status put_uint64(const std::string &key, std::uint64_t value) noexcept; status put_int64(const std::string &key, std::int64_t value) noexcept; status put_string(const std::string &key, const std::string &value) noexcept; template status get_data(const std::string &key, T *&value, std::size_t &number) const noexcept; template status get_object(const std::string &key, T *&value) const noexcept; status get_uint64(const std::string &key, std::uint64_t &value) const noexcept; status get_int64(const std::string &key, std::int64_t &value) const noexcept; status get_string(const std::string &key, std::string &value) const noexcept; pmemkv_config *release() noexcept; private: int init() noexcept; pmemkv_config *_config; }; /*! \class db \brief Main pmemkv class, it provides functions to operate on data in database. Database class for creating, opening and closing pmemkv's data file. It provides functions to write, read & remove data, count elements stored and check for existence of an element based on its key. */ class db { public: db() noexcept; ~db(); db(const db &other) = delete; db(db &&other) noexcept; db &operator=(const db &other) = delete; db &operator=(db &&other) noexcept; status open(const std::string &engine_name) noexcept; status open(const std::string &engine_name, config &&cfg) noexcept; void close() noexcept; status count_all(std::size_t &cnt) noexcept; status count_above(string_view key, std::size_t &cnt) noexcept; status count_equal_above(string_view key, std::size_t &cnt) noexcept; status count_equal_below(string_view key, std::size_t &cnt) noexcept; status count_below(string_view key, std::size_t &cnt) noexcept; status count_between(string_view key1, string_view key2, std::size_t &cnt) noexcept; status get_all(get_kv_callback *callback, void *arg) noexcept; status get_all(std::function f) noexcept; status get_above(string_view key, get_kv_callback *callback, void *arg) noexcept; status get_above(string_view key, std::function f) noexcept; status get_equal_above(string_view key, get_kv_callback *callback, void *arg) noexcept; status get_equal_above(string_view key, std::function f) noexcept; status get_equal_below(string_view key, get_kv_callback *callback, void *arg) noexcept; status get_equal_below(string_view key, std::function f) noexcept; status get_below(string_view key, get_kv_callback *callback, void *arg) noexcept; status get_below(string_view key, std::function f) noexcept; status get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) noexcept; status get_between(string_view key1, string_view key2, std::function f) noexcept; status exists(string_view key) noexcept; status get(string_view key, get_v_callback *callback, void *arg) noexcept; status get(string_view key, std::function f) noexcept; status get(string_view key, std::string *value) noexcept; status put(string_view key, string_view value) noexcept; status remove(string_view key) noexcept; status defrag(double start_percent = 0, double amount_percent = 100); private: pmemkv_db *_db; }; /** * Default constructor with uninitialized config. */ inline config::config() noexcept { this->_config = nullptr; } /** * Move constructor. Initializes config with another config. * Ownership is transferred to config class. */ inline config::config(config &&other) noexcept { this->_config = other._config; other._config = nullptr; } /** * Move assignment operator. Deletes previous config and replaces * it with another config. Ownership is transferred to config class. */ inline config &config::operator=(config &&other) noexcept { if (this->_config) pmemkv_config_delete(this->_config); this->_config = other._config; other._config = nullptr; return *this; } /** * Creates config from pointer to pmemkv_config. * Ownership is transferred to config class. */ inline config::config(pmemkv_config *cfg) noexcept { this->_config = cfg; } /** * Default destructor. Deletes config if initialized. */ inline config::~config() { if (this->_config) pmemkv_config_delete(this->_config); } /** * Initialization function for config. * It's lazy initialized and called within all put functions. * * @return int initialization result; 0 on success */ inline int config::init() noexcept { if (this->_config == nullptr) { this->_config = pmemkv_config_new(); if (this->_config == nullptr) return 1; } return 0; } /** * Puts binary data pointed by value, of type T, with count of elements * to a config. Count parameter is useful for putting arrays of data. * * @param[in] key The string representing config item's name. * @param[in] value The pointer to data. * @param[in] count The count of elements stored under reference. * * @return pmem::kv::status */ template inline status config::put_data(const std::string &key, const T *value, const std::size_t count) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast(pmemkv_config_put_data( this->_config, key.data(), (void *)value, count * sizeof(T))); } /** * Puts object pointed by value, of type T, with given destructor * to a config. * * @param[in] key The string representing config item's name. * @param[in] value The pointer to object. * @param[in] deleter The object's destructor function. * * @return pmem::kv::status */ template inline status config::put_object(const std::string &key, T *value, void (*deleter)(void *)) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast(pmemkv_config_put_object(this->_config, key.data(), (void *)value, deleter)); } /** * Puts std::uint64_t value to a config. * * @param[in] key The string representing config item's name. * @param[in] value The std::uint64_t value. * * @return pmem::kv::status */ inline status config::put_uint64(const std::string &key, std::uint64_t value) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast( pmemkv_config_put_uint64(this->_config, key.data(), value)); } /** * Puts std::int64_t value to a config. * * @param[in] key The string representing config item's name. * @param[in] value The std::int64_t value. * * @return pmem::kv::status */ inline status config::put_int64(const std::string &key, std::int64_t value) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast( pmemkv_config_put_int64(this->_config, key.data(), value)); } /** * Puts string value to a config. * * @param[in] key The string representing config item's name. * @param[in] value The string value. * * @return pmem::kv::status */ inline status config::put_string(const std::string &key, const std::string &value) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast( pmemkv_config_put_string(this->_config, key.data(), value.data())); } /** * Gets object from a config item with key name and copies it * into T object value. * * @param[in] key The string representing config item's name. * @param[out] value The pointer to data. * @param[out] count The count of elements stored under reference. * * @return pmem::kv::status */ template inline status config::get_data(const std::string &key, T *&value, std::size_t &count) const noexcept { if (this->_config == nullptr) return status::NOT_FOUND; std::size_t size; auto s = static_cast(pmemkv_config_get_data( this->_config, key.data(), (const void **)&value, &size)); if (s != status::OK) return s; count = size / sizeof(T); return status::OK; } /** * Gets binary data from a config item with key name and * assigns pointer to T object value. * * @param[in] key The string representing config item's name. * @param[out] value The pointer to object. * * @return pmem::kv::status */ template inline status config::get_object(const std::string &key, T *&value) const noexcept { if (this->_config == nullptr) return status::NOT_FOUND; auto s = static_cast( pmemkv_config_get_object(this->_config, key.data(), (void **)&value)); return s; } /** * Gets std::uint64_t value from a config item with key name. * * @param[in] key The string representing config item's name. * @param[out] value The std::uint64_t value. * * @return pmem::kv::status */ inline status config::get_uint64(const std::string &key, std::uint64_t &value) const noexcept { if (this->_config == nullptr) return status::NOT_FOUND; return static_cast( pmemkv_config_get_uint64(this->_config, key.data(), &value)); } /** * Gets std::int64_t value from a config item with key name. * * @param[in] key The string representing config item's name. * @param[out] value The std::int64_t value. * * @return pmem::kv::status */ inline status config::get_int64(const std::string &key, std::int64_t &value) const noexcept { if (this->_config == nullptr) return status::NOT_FOUND; return static_cast( pmemkv_config_get_int64(this->_config, key.data(), &value)); } /** * Gets string value from a config item with key name. * * @param[in] key The string representing config item's name. * @param[out] value The string value. * * @return pmem::kv::status */ inline status config::get_string(const std::string &key, std::string &value) const noexcept { if (this->_config == nullptr) return status::NOT_FOUND; const char *data; auto s = static_cast( pmemkv_config_get_string(this->_config, key.data(), &data)); if (s != status::OK) return s; value = data; return status::OK; } /** * Similarly to std::unique_ptr::release it passes the ownership * of underlying pmemkv_config variable and sets it to nullptr. * * @return handle to pmemkv_config */ inline pmemkv_config *config::release() noexcept { auto c = this->_config; this->_config = nullptr; return c; } #if !__cpp_lib_string_view /** * Default constructor with empty data. */ inline string_view::string_view() noexcept : _data(""), _size(0) { } /** * Constructor initialized by *data* and its *size*. * * @param[in] data pointer to the C-like string (char *) to initialize with, * it can contain null characters * @param[in] size length of the given data */ inline string_view::string_view(const char *data, size_t size) : _data(data), _size(size) { } /** * Constructor initialized by the string *s*. * * @param[in] s reference to the string to initialize with */ inline string_view::string_view(const std::string &s) : _data(s.c_str()), _size(s.size()) { } /** * Constructor initialized by *data*. Size of the data will be set * using std::char_traits::length(). * * @param[in] data pointer to C-like string (char *) to initialize with, * it has to end with the terminating null character */ inline string_view::string_view(const char *data) : _data(data), _size(std::char_traits::length(data)) { } /** * Returns pointer to data stored in this pmem::kv::string_view. It may not contain * the terminating null character. * * @return pointer to C-like string (char *), it may not end with null character */ inline const char *string_view::data() const noexcept { return _data; } /** * Returns count of characters stored in this pmem::kv::string_view data. * * @return pointer to C-like string (char *), it may not end with null character */ inline std::size_t string_view::size() const noexcept { return _size; } /** * Compares this string_view with other. Works in the same way as * std::basic_string::compare. * * @return 0 if both character sequences compare equal, * positive value if this is lexicographically greater than other, * negative value if this is lexicographically less than other. */ inline int string_view::compare(const string_view &other) noexcept { int ret = std::char_traits::compare(data(), other.data(), std::min(size(), other.size())); if (ret != 0) return ret; if (size() < other.size()) return -1; if (size() > other.size()) return 1; return 0; } #endif /* * All functions which will be called by C code must be declared as extern "C" * to ensure they have C linkage. It is needed because it is possible that * C and C++ functions use different calling conventions. */ extern "C" { static inline int call_get_kv_function(const char *key, size_t keybytes, const char *value, size_t valuebytes, void *arg) { return (*reinterpret_cast *>(arg))( string_view(key, keybytes), string_view(value, valuebytes)); } static inline void call_get_v_function(const char *value, size_t valuebytes, void *arg) { (*reinterpret_cast *>(arg))( string_view(value, valuebytes)); } static inline void call_get_copy(const char *v, size_t vb, void *arg) { auto c = reinterpret_cast(arg); c->assign(v, vb); } } /** * Default constructor with uninitialized database. */ inline db::db() noexcept { this->_db = nullptr; } /** * Move constructor. Initializes database with another database. * Ownership is being transferred to a class that move constructor was called on. * * @param[in] other another database, to be moved from */ inline db::db(db &&other) noexcept { this->_db = other._db; other._db = nullptr; } /** * Move assignment operator. Deletes previous database and replaces * it with another database. Ownership is being transferred to a class that * assign operator was called on. * * @param[in] other another database, to be assigned from */ inline db &db::operator=(db &&other) noexcept { close(); std::swap(this->_db, other._db); return *this; } /** * Opens the pmemkv database without any configuration parameters. * * @param[in] engine_name name of the engine to work with * * @return pmem::kv::status */ inline status db::open(const std::string &engine_name) noexcept { return static_cast( pmemkv_open(engine_name.c_str(), nullptr, &(this->_db))); } /** * Opens the pmemkv database with specified config. * * @param[in] engine_name name of the engine to work with * @param[in] cfg pmem::kv::config with parameters specified for the engine * * @return pmem::kv::status */ inline status db::open(const std::string &engine_name, config &&cfg) noexcept { return static_cast( pmemkv_open(engine_name.c_str(), cfg.release(), &(this->_db))); } /** * Closes pmemkv database. */ inline void db::close() noexcept { if (this->_db != nullptr) pmemkv_close(this->_db); this->_db = nullptr; } /** * Default destructor. Closes pmemkv database. */ inline db::~db() { close(); } /** * It returns number of currently stored elements in pmem::kv::db. * * @param[out] cnt number of records stored in pmem::kv::db. * * @return pmem::kv::status */ inline status db::count_all(std::size_t &cnt) noexcept { return static_cast(pmemkv_count_all(this->_db, &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are greater than the given *key*. * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound of counting * @param[out] cnt number of records in pmem::kv::db matching query * * @return pmem::kv::status */ inline status db::count_above(string_view key, std::size_t &cnt) noexcept { return static_cast( pmemkv_count_above(this->_db, key.data(), key.size(), &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are greater than or equal to the given *key*. * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound of counting * @param[out] cnt number of records in pmem::kv::db matching query * * @return pmem::kv::status */ inline status db::count_equal_above(string_view key, std::size_t &cnt) noexcept { return static_cast( pmemkv_count_equal_above(this->_db, key.data(), key.size(), &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are lower than or equal to the given *key*. * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound of counting * @param[out] cnt number of records in pmem::kv::db matching query * * @return pmem::kv::status */ inline status db::count_equal_below(string_view key, std::size_t &cnt) noexcept { return static_cast( pmemkv_count_equal_below(this->_db, key.data(), key.size(), &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are less than the given *key*. * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the upper bound of counting * @param[out] cnt number of records in pmem::kv::db matching query * * @return pmem::kv::status */ inline status db::count_below(string_view key, std::size_t &cnt) noexcept { return static_cast( pmemkv_count_below(this->_db, key.data(), key.size(), &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are greater than the *key1* and less than the *key2*. * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key1 sets the lower bound of counting * @param[in] key2 sets the upper bound of counting * @param[out] cnt number of records in pmem::kv::db matching query * * @return pmem::kv::status */ inline status db::count_between(string_view key1, string_view key2, std::size_t &cnt) noexcept { return static_cast(pmemkv_count_between( this->_db, key1.data(), key1.size(), key2.data(), key2.size(), &cnt)); } /** * Executes (C-like) *callback* function for every record stored in pmem::kv::db. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case *get_all()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * @param[in] callback function to be called for every element stored in db * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_all(get_kv_callback *callback, void *arg) noexcept { return static_cast(pmemkv_get_all(this->_db, callback, arg)); } /** * Executes function for every record stored in pmem::kv::db. * Callback can stop iteration by returning non-zero value. In that case *get_all()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_all(std::function f) noexcept { return static_cast(pmemkv_get_all(this->_db, call_get_kv_function, &f)); } /** * Executes (C-like) callback function for every record stored in pmem::kv::db, * whose keys are greater than the given *key*. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case *get_above()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound for querying * @param[in] callback function to be called for each returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_above(string_view key, get_kv_callback *callback, void *arg) noexcept { return static_cast( pmemkv_get_above(this->_db, key.data(), key.size(), callback, arg)); } /** * Executes function for every record stored in pmem::kv::db, whose keys are * greater than the given *key*. * Callback can stop iteration by returning non-zero value. In that case *get_above()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound for querying * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_above(string_view key, std::function f) noexcept { return static_cast(pmemkv_get_above(this->_db, key.data(), key.size(), call_get_kv_function, &f)); } /** * Executes (C-like) callback function for every record stored in pmem::kv::db, * whose keys are greater than or equal to the given *key*. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case * *get_equal_above()* returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues * iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound for querying * @param[in] callback function to be called for each returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_equal_above(string_view key, get_kv_callback *callback, void *arg) noexcept { return static_cast( pmemkv_get_equal_above(this->_db, key.data(), key.size(), callback, arg)); } /** * Executes function for every record stored in pmem::kv::db, whose keys are * greater than or equal to the given *key*. * Callback can stop iteration by returning non-zero value. In that case **get_equal_above()* returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues *iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the lower bound for querying * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_equal_above(string_view key, std::function f) noexcept { return static_cast(pmemkv_get_equal_above( this->_db, key.data(), key.size(), call_get_kv_function, &f)); } /** * Executes (C-like) callback function for every record stored in pmem::kv::db, * whose keys are lower than or equal to the given *key*. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case * *get_equal_below()* returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues * iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the upper bound for querying * @param[in] callback function to be called for each returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_equal_below(string_view key, get_kv_callback *callback, void *arg) noexcept { return static_cast( pmemkv_get_equal_below(this->_db, key.data(), key.size(), callback, arg)); } /** * Executes function for every record stored in pmem::kv::db, whose keys are * lower than or equal to the given *key*. * Callback can stop iteration by returning non-zero value. In that case **get_equal_below()* returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues *iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the upper bound for querying * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_equal_below(string_view key, std::function f) noexcept { return static_cast(pmemkv_get_equal_below( this->_db, key.data(), key.size(), call_get_kv_function, &f)); } /** * Executes (C-like) callback function for every record stored in pmem::kv::db, * whose keys are lower than the given *key*. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case *get_below()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the upper bound for querying * @param[in] callback function to be called for each returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_below(string_view key, get_kv_callback *callback, void *arg) noexcept { return static_cast( pmemkv_get_below(this->_db, key.data(), key.size(), callback, arg)); } /** * Executes function for every record stored in pmem::kv::db, whose keys are * less than the given *key*. * Callback can stop iteration by returning non-zero value. In that case *get_below()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key sets the upper bound for querying * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_below(string_view key, std::function f) noexcept { return static_cast(pmemkv_get_below(this->_db, key.data(), key.size(), call_get_kv_function, &f)); } /** * Executes (C-like) callback function for every record stored in pmem::kv::db, * whose keys are greater than the *key1* and less than the *key2*. * Arguments passed to the callback function are: pointer to a key, size of the * key, pointer to a value, size of the value and *arg* specified by the user. * Callback can stop iteration by returning non-zero value. In that case *get_between()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key1 sets the lower bound for querying * @param[in] key2 sets the upper bound for querying * @param[in] callback function to be called for each returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) noexcept { return static_cast(pmemkv_get_between(this->_db, key1.data(), key1.size(), key2.data(), key2.size(), callback, arg)); } /** * Executes function for every record stored in pmem::kv::db, whose keys * are greater than the *key1* and less than the *key2*. * Callback can stop iteration by returning non-zero value. In that case *get_between()* * returns pmem::kv::status::STOPPED_BY_CB. Returning 0 continues iteration. * * Keys are sorted in lexicographical order (see std::lexicographical_compare). * * @param[in] key1 sets the lower bound for querying * @param[in] key2 sets the upper bound for querying * @param[in] f function called for each returned element, it is called with params: * key and value * * @return pmem::kv::status */ inline status db::get_between(string_view key1, string_view key2, std::function f) noexcept { return static_cast(pmemkv_get_between(this->_db, key1.data(), key1.size(), key2.data(), key2.size(), call_get_kv_function, &f)); } /** * Checks existence of record with given *key*. If record is present * pmem::kv::status::OK is returned, otherwise pmem::kv::status::NOT_FOUND * is returned. Other possible return values are described in pmem::kv::status. * * @param[in] key record's key to query for in database * * @return pmem::kv::status */ inline status db::exists(string_view key) noexcept { return static_cast(pmemkv_exists(this->_db, key.data(), key.size())); } /** * Executes (C-like) *callback* function for record with given *key*. If * record is present and no error occurred, the function returns * pmem::kv::status::OK. If record does not exist pmem::kv::status::NOT_FOUND * is returned. Other possible return values are described in pmem::kv::status. * *Callback* is called with the following parameters: * pointer to a value, size of the value and *arg* specified by the user. * This function is guaranteed to be implemented by all engines. * * @param[in] key record's key to query for * @param[in] callback function to be called for returned element * @param[in] arg additional arguments to be passed to callback * * @return pmem::kv::status */ inline status db::get(string_view key, get_v_callback *callback, void *arg) noexcept { return static_cast( pmemkv_get(this->_db, key.data(), key.size(), callback, arg)); } /** * Executes function for record with given *key*. If record is present and * no error occurred the function returns pmem::kv::status::OK. If record does * not exist pmem::kv::status::NOT_FOUND is returned. * * @param[in] key record's key to query for * @param[in] f function called for returned element, it is called with only * one param - value (key is known) * * @return pmem::kv::status */ inline status db::get(string_view key, std::function f) noexcept { return static_cast( pmemkv_get(this->_db, key.data(), key.size(), call_get_v_function, &f)); } /** * Gets value copy of record with given *key*. In absence of any errors, * pmem::kv::status::OK is returned. * This function is guaranteed to be implemented by all engines. * * @param[in] key record's key to query for * @param[out] value stores returned copy of the data * * @return pmem::kv::status */ inline status db::get(string_view key, std::string *value) noexcept { return static_cast( pmemkv_get(this->_db, key.data(), key.size(), call_get_copy, value)); } /** * Inserts a key-value pair into pmemkv database. * This function is guaranteed to be implemented by all engines. * * @param[in] key record's key; record will be put into database under its name * @param[in] value data to be inserted into this new database record * * @return pmem::kv::status */ inline status db::put(string_view key, string_view value) noexcept { return static_cast(pmemkv_put(this->_db, key.data(), key.size(), value.data(), value.size())); } /** * Removes from database record with given *key*. * This function is guaranteed to be implemented by all engines. * * @param[in] key record's key to query for, to be removed * * @return pmem::kv::status */ inline status db::remove(string_view key) noexcept { return static_cast(pmemkv_remove(this->_db, key.data(), key.size())); } /** * Defragments approximately 'amount_percent' percent of elements * in the database starting from 'start_percent' percent of elements. * * @param[in] start_percent starting percent of elements to defragment from * @param[in] amount_percent amount percent of elements to defragment * * @return pmem::kv::status */ inline status db::defrag(double start_percent, double amount_percent) { return static_cast( pmemkv_defrag(this->_db, start_percent, amount_percent)); } /** * Returns a human readable string describing the last error. */ static inline std::string errormsg() { return std::string(pmemkv_errormsg()); } } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_HPP */ pmemkv-1.1/src/libpmemkv.map000066400000000000000000000045521361504041500161330ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # # src/libpmemkv.map -- linker map file for libpmemkv # LIBPMEMKV_1.0 { global: pmemkv_close; pmemkv_config_delete; pmemkv_config_get_data; pmemkv_config_get_int64; pmemkv_config_get_object; pmemkv_config_get_string; pmemkv_config_get_uint64; pmemkv_config_new; pmemkv_config_put_data; pmemkv_config_put_int64; pmemkv_config_put_object; pmemkv_config_put_string; pmemkv_config_put_uint64; pmemkv_count_above; pmemkv_count_all; pmemkv_count_below; pmemkv_count_between; pmemkv_count_equal_above; pmemkv_count_equal_below; pmemkv_defrag; pmemkv_errormsg; pmemkv_exists; pmemkv_get; pmemkv_get_above; pmemkv_get_all; pmemkv_get_below; pmemkv_get_between; pmemkv_get_copy; pmemkv_get_equal_above; pmemkv_get_equal_below; pmemkv_open; pmemkv_put; pmemkv_remove; local: *; }; pmemkv-1.1/src/libpmemkv_json_config.cc000066400000000000000000000104461361504041500203200ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "libpmemkv_json_config.h" #include "out.h" #include #include #include #include extern "C" { int pmemkv_config_from_json(pmemkv_config *config, const char *json) { rapidjson::Document doc; rapidjson::Value::ConstMemberIterator itr; assert(config && json); try { if (doc.Parse(json).HasParseError()) { throw std::runtime_error("Config parsing failed"); } for (itr = doc.MemberBegin(); itr != doc.MemberEnd(); ++itr) { if (itr->value.IsString()) { auto value = itr->value.GetString(); auto status = pmemkv_config_put_string( config, itr->name.GetString(), value); if (status != PMEMKV_STATUS_OK) throw std::runtime_error( "Inserting string to the config failed"); } else if (itr->value.IsInt64()) { auto value = itr->value.GetInt64(); auto status = pmemkv_config_put_int64( config, itr->name.GetString(), value); if (status != PMEMKV_STATUS_OK) throw std::runtime_error( "Inserting int to the config failed"); } else if (itr->value.IsTrue() || itr->value.IsFalse()) { auto value = itr->value.GetBool(); auto status = pmemkv_config_put_int64( config, itr->name.GetString(), value); if (status != PMEMKV_STATUS_OK) throw std::runtime_error( "Inserting bool to the config failed"); } else if (itr->value.IsObject()) { rapidjson::StringBuffer sb; rapidjson::Writer writer(sb); itr->value.Accept(writer); auto sub_cfg = pmemkv_config_new(); if (sub_cfg == nullptr) { ERR() << "Cannot allocate subconfig"; return PMEMKV_STATUS_OUT_OF_MEMORY; } auto status = pmemkv_config_from_json(sub_cfg, sb.GetString()); if (status != PMEMKV_STATUS_OK) { pmemkv_config_delete(sub_cfg); throw std::runtime_error( "Cannot parse subconfig"); } status = pmemkv_config_put_object( config, itr->name.GetString(), sub_cfg, (void (*)(void *)) & pmemkv_config_delete); if (status != PMEMKV_STATUS_OK) throw std::runtime_error( "Inserting a new entry to the config failed"); } else { static std::string kTypeNames[] = { "Null", "False", "True", "Object", "Array", "String", "Number"}; throw std::runtime_error( "Unsupported data type in JSON string: " + kTypeNames[itr->value.GetType()]); } } } catch (const std::exception &exc) { ERR() << exc.what(); return PMEMKV_STATUS_CONFIG_PARSING_ERROR; } catch (...) { ERR() << "Unspecified failure"; return PMEMKV_STATUS_CONFIG_PARSING_ERROR; } return PMEMKV_STATUS_OK; } const char *pmemkv_config_from_json_errormsg(void) { return out_get_errormsg(); } } /* extern "C" */ pmemkv-1.1/src/libpmemkv_json_config.h000066400000000000000000000036011361504041500201550ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_JSON_H #define LIBPMEMKV_JSON_H #include "libpmemkv.h" #ifdef __cplusplus extern "C" { #endif int pmemkv_config_from_json(pmemkv_config *config, const char *jsonconfig); const char *pmemkv_config_from_json_errormsg(void); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* LIBPMEMKV_JSON_H */ pmemkv-1.1/src/libpmemkv_json_config.map000066400000000000000000000033411361504041500205040ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # # src/libpmemkv_json_config.map -- linker map file for libpmemkv_json_config # LIBPMEMKV_JSON_CONFIG_1.0 { global: pmemkv_config_from_json; pmemkv_config_from_json_errormsg; local: *; }; pmemkv-1.1/src/out.cc000066400000000000000000000036721361504041500145660ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "out.h" #include #include static thread_local std::stringstream error_stream; static thread_local std::string str; std::ostream &out_err_stream(const char *func) { error_stream.str(std::string()); error_stream << "[" << func << "] "; return error_stream; } const char *out_get_errormsg(void) { str = error_stream.str(); return str.c_str(); } pmemkv-1.1/src/out.h000066400000000000000000000041761361504041500144300ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_OUT_H #define LIBPMEMKV_OUT_H #include std::ostream &out_err_stream(const char *func); #define DO_LOG 0 #define LOG(msg) \ do { \ if (DO_LOG) \ std::cout << "[" << name() << "] " << msg << "\n"; \ } while (0) #define ERR() out_err_stream(__func__) const char *out_get_errormsg(void); #endif /* LIBPMEMKV_OUT_H */ pmemkv-1.1/src/pmemobj_engine.h000066400000000000000000000071071361504041500165740ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef LIBPMEMKV_PMEMOBJ_ENGINE_H #define LIBPMEMKV_PMEMOBJ_ENGINE_H #include #include #include "engine.h" #include "libpmemkv.h" #include namespace pmem { /* Helper method which throws an exception when called in a tx */ static inline void check_outside_tx() { if (pmemobj_tx_stage() != TX_STAGE_NONE) throw transaction_scope_error( "Function called inside transaction scope."); } namespace kv { template class pmemobj_engine_base : public engine_base { public: pmemobj_engine_base(std::unique_ptr &cfg) { const char *path = nullptr; std::size_t size; PMEMoid *oid; auto is_path = cfg->get_string("path", &path); auto is_oid = cfg->get_object("oid", (void **)&oid); if (is_path && is_oid) { throw internal::invalid_argument( "Config contains both: \"path\" and \"oid\""); } else if (!is_path && !is_oid) { throw internal::invalid_argument( "Config does not contain item with key: \"path\" or \"oid\""); } else if (is_path) { uint64_t force_create; cfg_by_path = true; if (!cfg->get_uint64("force_create", &force_create)) { force_create = 0; } pmem::obj::pool pop; if (force_create) { if (!cfg->get_uint64("size", &size)) throw internal::invalid_argument( "Config does not contain item with key: \"size\""); pop = pmem::obj::pool::create(path, LAYOUT, size, S_IRWXU); } else { pop = pmem::obj::pool::open(path, LAYOUT); } root_oid = pop.root()->ptr.raw_ptr(); pmpool = pop; } else if (is_oid) { pmpool = pmem::obj::pool_base(pmemobj_pool_by_ptr(oid)); root_oid = oid; } } ~pmemobj_engine_base() { if (cfg_by_path) pmpool.close(); } protected: struct Root { pmem::obj::persistent_ptr ptr; /* used when path is specified */ }; pmem::obj::pool_base pmpool; PMEMoid *root_oid; bool cfg_by_path = false; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_PMEMOBJ_ENGINE_H */ pmemkv-1.1/src/polymorphic_string.h000066400000000000000000000074431361504041500175540ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once #include #include #include #include "libpmemkv.hpp" namespace pmem { namespace kv { class polymorphic_string { public: using pmem_string = pmem::obj::string; polymorphic_string() { } polymorphic_string(const char *data, std::size_t size) : pstr(data, size) { } polymorphic_string(const std::string &s) : pstr(s) { } polymorphic_string(const pmem_string &s) : pstr(s) { } polymorphic_string(string_view s) : pstr(s.data(), s.size()) { } polymorphic_string(const polymorphic_string &s) : pstr(s.pstr) { } polymorphic_string &operator=(const std::string &s) { pstr = s; return *this; } polymorphic_string &operator=(const pmem_string &s) { check_pmem(); pstr = s; return *this; } polymorphic_string &operator=(const polymorphic_string &s) { this->operator=(s.pstr); return *this; } polymorphic_string &operator=(string_view s) { pstr.assign(s.data(), s.size()); return *this; } char &operator[](size_t n) { return pstr[n]; } const char &operator[](size_t n) const { return pstr[n]; } const char *c_str() const { return pstr.c_str(); } size_t size() const { return pstr.size(); } size_t length() const { return size(); } bool empty() const { return size() == 0; } bool operator==(const polymorphic_string &rhs) const { return compare(0U, size(), rhs.c_str(), rhs.size()) == 0; } bool operator==(string_view rhs) const { return compare(0U, size(), rhs.data(), rhs.size()) == 0; } bool operator==(const std::string &rhs) const { return compare(0U, size(), rhs.c_str(), rhs.size()) == 0; } template int compare(Args &&... args) const { return pstr.compare(std::forward(args)...); } private: /* required for layout in compatibility purpose */ pmem::obj::p unused = true; pmem_string pstr; void check_pmem() const { assert(pmemobj_pool_by_ptr(this) != nullptr); } }; // class polymorphic_string inline bool operator==(string_view lhs, const polymorphic_string &rhs) { return rhs == lhs; } inline bool operator==(const std::string &lhs, const polymorphic_string &rhs) { return rhs == lhs; } } /* namespace kv */ } /* namespace pmem */ pmemkv-1.1/src/valgrind/000077500000000000000000000000001361504041500152465ustar00rootroot00000000000000pmemkv-1.1/src/valgrind/README000066400000000000000000000001331361504041500161230ustar00rootroot00000000000000Files in this directory were imported from Valgrind 3.14: https://github.com/pmem/valgrind pmemkv-1.1/src/valgrind/drd.h000066400000000000000000000547061361504041500162040ustar00rootroot00000000000000/* ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (drd.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 DRD, a Valgrind tool for verification of multithreaded programs. Copyright (C) 2006-2017 Bart Van Assche . 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 (drd.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. ---------------------------------------------------------------- */ #ifndef __VALGRIND_DRD_H #define __VALGRIND_DRD_H #include "valgrind.h" /** Obtain the thread ID assigned by Valgrind's core. */ #define DRD_GET_VALGRIND_THREADID \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__DRD_GET_VALGRIND_THREAD_ID, \ 0, 0, 0, 0, 0) /** Obtain the thread ID assigned by DRD. */ #define DRD_GET_DRD_THREADID \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__DRD_GET_DRD_THREAD_ID, \ 0, 0, 0, 0, 0) /** Tell DRD not to complain about data races for the specified variable. */ #define DRD_IGNORE_VAR(x) ANNOTATE_BENIGN_RACE_SIZED(&(x), sizeof(x), "") /** Tell DRD to no longer ignore data races for the specified variable. */ #define DRD_STOP_IGNORING_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_FINISH_SUPPRESSION, \ &(x), sizeof(x), 0, 0, 0) /** * Tell DRD to trace all memory accesses for the specified variable * until the memory that was allocated for the variable is freed. */ #define DRD_TRACE_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_START_TRACE_ADDR, \ &(x), sizeof(x), 0, 0, 0) /** * Tell DRD to stop tracing memory accesses for the specified variable. */ #define DRD_STOP_TRACING_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_STOP_TRACE_ADDR, \ &(x), sizeof(x), 0, 0, 0) /** * @defgroup RaceDetectionAnnotations Data race detection annotations. * * @see See also the source file producer-consumer. */ #define ANNOTATE_PCQ_CREATE(pcq) do { } while(0) /** Tell DRD that a FIFO queue has been destroyed. */ #define ANNOTATE_PCQ_DESTROY(pcq) do { } while(0) /** * Tell DRD that an element has been added to the FIFO queue at address pcq. */ #define ANNOTATE_PCQ_PUT(pcq) do { } while(0) /** * Tell DRD that an element has been removed from the FIFO queue at address pcq, * and that DRD should insert a happens-before relationship between the memory * accesses that occurred before the corresponding ANNOTATE_PCQ_PUT(pcq) * annotation and the memory accesses after this annotation. Correspondence * between PUT and GET annotations happens in FIFO order. Since locking * of the queue is needed anyway to add elements to or to remove elements from * the queue, for DRD all four FIFO annotations are defined as no-ops. */ #define ANNOTATE_PCQ_GET(pcq) do { } while(0) /** * Tell DRD that data races at the specified address are expected and must not * be reported. */ #define ANNOTATE_BENIGN_RACE(addr, descr) \ ANNOTATE_BENIGN_RACE_SIZED(addr, sizeof(*addr), descr) /* Same as ANNOTATE_BENIGN_RACE(addr, descr), but applies to the memory range [addr, addr + size). */ #define ANNOTATE_BENIGN_RACE_SIZED(addr, size, descr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_START_SUPPRESSION, \ addr, size, 0, 0, 0) /** Tell DRD to ignore all reads performed by the current thread. */ #define ANNOTATE_IGNORE_READS_BEGIN() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_LOADS, \ 0, 0, 0, 0, 0); /** Tell DRD to no longer ignore the reads performed by the current thread. */ #define ANNOTATE_IGNORE_READS_END() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_LOADS, \ 1, 0, 0, 0, 0); /** Tell DRD to ignore all writes performed by the current thread. */ #define ANNOTATE_IGNORE_WRITES_BEGIN() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_STORES, \ 0, 0, 0, 0, 0) /** Tell DRD to no longer ignore the writes performed by the current thread. */ #define ANNOTATE_IGNORE_WRITES_END() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_STORES, \ 1, 0, 0, 0, 0) /** Tell DRD to ignore all memory accesses performed by the current thread. */ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ do { ANNOTATE_IGNORE_READS_BEGIN(); ANNOTATE_IGNORE_WRITES_BEGIN(); } while(0) /** * Tell DRD to no longer ignore the memory accesses performed by the current * thread. */ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ do { ANNOTATE_IGNORE_READS_END(); ANNOTATE_IGNORE_WRITES_END(); } while(0) /** * Tell DRD that size bytes starting at addr has been allocated by a custom * memory allocator. */ #define ANNOTATE_NEW_MEMORY(addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_CLEAN_MEMORY, \ addr, size, 0, 0, 0) /** Ask DRD to report every access to the specified address. */ #define ANNOTATE_TRACE_MEMORY(addr) DRD_TRACE_VAR(*(char*)(addr)) /** * Tell DRD to assign the specified name to the current thread. This name will * be used in error messages printed by DRD. */ #define ANNOTATE_THREAD_NAME(name) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_SET_THREAD_NAME, \ name, 0, 0, 0, 0) /*@}*/ /* !! 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. */ enum { /* Ask the DRD tool to discard all information about memory accesses */ /* and client objects for the specified range. This client request is */ /* binary compatible with the similarly named Helgrind client request. */ VG_USERREQ__DRD_CLEAN_MEMORY = VG_USERREQ_TOOL_BASE('H','G'), /* args: Addr, SizeT. */ /* Ask the DRD tool the thread ID assigned by Valgrind. */ VG_USERREQ__DRD_GET_VALGRIND_THREAD_ID = VG_USERREQ_TOOL_BASE('D','R'), /* args: none. */ /* Ask the DRD tool the thread ID assigned by DRD. */ VG_USERREQ__DRD_GET_DRD_THREAD_ID, /* args: none. */ /* To tell the DRD tool to suppress data race detection on the */ /* specified address range. */ VG_USERREQ__DRD_START_SUPPRESSION, /* args: start address, size in bytes */ /* To tell the DRD tool no longer to suppress data race detection on */ /* the specified address range. */ VG_USERREQ__DRD_FINISH_SUPPRESSION, /* args: start address, size in bytes */ /* To ask the DRD tool to trace all accesses to the specified range. */ VG_USERREQ__DRD_START_TRACE_ADDR, /* args: Addr, SizeT. */ /* To ask the DRD tool to stop tracing accesses to the specified range. */ VG_USERREQ__DRD_STOP_TRACE_ADDR, /* args: Addr, SizeT. */ /* Tell DRD whether or not to record memory loads in the calling thread. */ VG_USERREQ__DRD_RECORD_LOADS, /* args: Bool. */ /* Tell DRD whether or not to record memory stores in the calling thread. */ VG_USERREQ__DRD_RECORD_STORES, /* args: Bool. */ /* Set the name of the thread that performs this client request. */ VG_USERREQ__DRD_SET_THREAD_NAME, /* args: null-terminated character string. */ /* Tell DRD that a DRD annotation has not yet been implemented. */ VG_USERREQ__DRD_ANNOTATION_UNIMP, /* args: char*. */ /* Tell DRD that a user-defined semaphore synchronization object * is about to be created. */ VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE, /* args: Addr, UInt value. */ /* Tell DRD that a user-defined semaphore synchronization object * has been destroyed. */ VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object is going to be acquired (semaphore wait). */ VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object has been acquired (semaphore wait). */ VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object is about to be released (semaphore post). */ VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE, /* args: Addr. */ /* Tell DRD to ignore the inter-thread ordering introduced by a mutex. */ VG_USERREQ__DRD_IGNORE_MUTEX_ORDERING, /* args: Addr. */ /* Tell DRD that a user-defined reader-writer synchronization object * has been created. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_CREATE = VG_USERREQ_TOOL_BASE('H','G') + 256 + 14, /* args: Addr. */ /* Tell DRD that a user-defined reader-writer synchronization object * is about to be destroyed. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_DESTROY = VG_USERREQ_TOOL_BASE('H','G') + 256 + 15, /* args: Addr. */ /* Tell DRD that a lock on a user-defined reader-writer synchronization * object has been acquired. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_ACQUIRED = VG_USERREQ_TOOL_BASE('H','G') + 256 + 17, /* args: Addr, Int is_rw. */ /* Tell DRD that a lock on a user-defined reader-writer synchronization * object is about to be released. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_RELEASED = VG_USERREQ_TOOL_BASE('H','G') + 256 + 18, /* args: Addr, Int is_rw. */ /* Tell DRD that a Helgrind annotation has not yet been implemented. */ VG_USERREQ__HELGRIND_ANNOTATION_UNIMP = VG_USERREQ_TOOL_BASE('H','G') + 256 + 32, /* args: char*. */ /* Tell DRD to insert a happens-before annotation. */ VG_USERREQ__DRD_ANNOTATE_HAPPENS_BEFORE = VG_USERREQ_TOOL_BASE('H','G') + 256 + 33, /* args: Addr. */ /* Tell DRD to insert a happens-after annotation. */ VG_USERREQ__DRD_ANNOTATE_HAPPENS_AFTER = VG_USERREQ_TOOL_BASE('H','G') + 256 + 34, /* args: Addr. */ }; /** * @addtogroup RaceDetectionAnnotations */ /*@{*/ #ifdef __cplusplus /* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racy reads. Instead of doing ANNOTATE_IGNORE_READS_BEGIN(); ... = x; ANNOTATE_IGNORE_READS_END(); one can use ... = ANNOTATE_UNPROTECTED_READ(x); */ template inline T ANNOTATE_UNPROTECTED_READ(const volatile T& x) { ANNOTATE_IGNORE_READS_BEGIN(); const T result = x; ANNOTATE_IGNORE_READS_END(); return result; } /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ namespace { \ static class static_var##_annotator \ { \ public: \ static_var##_annotator() \ { \ ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ #static_var ": " description); \ } \ } the_##static_var##_annotator; \ } #endif /*@}*/ #endif /* __VALGRIND_DRD_H */ pmemkv-1.1/src/valgrind/helgrind.h000066400000000000000000001137331361504041500172230ustar00rootroot00000000000000/* ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (helgrind.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 part of Helgrind, a Valgrind tool for detecting errors in threaded programs. Copyright (C) 2007-2017 OpenWorks LLP info@open-works.co.uk 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 (helgrind.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. ---------------------------------------------------------------- */ #ifndef __HELGRIND_H #define __HELGRIND_H #include "valgrind.h" /* !! 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__HG_CLEAN_MEMORY = VG_USERREQ_TOOL_BASE('H','G'), /* The rest are for Helgrind's internal use. Not for end-user use. Do not use them unless you are a Valgrind developer. */ /* Notify the tool what this thread's pthread_t is. */ _VG_USERREQ__HG_SET_MY_PTHREAD_T = VG_USERREQ_TOOL_BASE('H','G') + 256, _VG_USERREQ__HG_PTH_API_ERROR, /* char*, int */ _VG_USERREQ__HG_PTHREAD_JOIN_POST, /* pthread_t of quitter */ _VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, /* pth_mx_t*, long mbRec */ _VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, /* pth_mx_t*, long isInit */ _VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, /* pth_mx_t* */ _VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, /* pth_mx_t* */ _VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_PRE, /* void*, long isTryLock */ _VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_POST, /* void* */ _VG_USERREQ__HG_PTHREAD_COND_SIGNAL_PRE, /* pth_cond_t* */ _VG_USERREQ__HG_PTHREAD_COND_BROADCAST_PRE, /* pth_cond_t* */ _VG_USERREQ__HG_PTHREAD_COND_WAIT_PRE, /* pth_cond_t*, pth_mx_t* */ _VG_USERREQ__HG_PTHREAD_COND_WAIT_POST, /* pth_cond_t*, pth_mx_t* */ _VG_USERREQ__HG_PTHREAD_COND_DESTROY_PRE, /* pth_cond_t*, long isInit */ _VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, /* pth_rwlk_t* */ _VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, /* pth_rwlk_t* */ _VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE, /* pth_rwlk_t*, long isW */ _VG_USERREQ__HG_PTHREAD_RWLOCK_ACQUIRED, /* void*, long isW */ _VG_USERREQ__HG_PTHREAD_RWLOCK_RELEASED, /* void **/ _VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_POST, /* pth_rwlk_t* */ _VG_USERREQ__HG_POSIX_SEM_INIT_POST, /* sem_t*, ulong value */ _VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_RELEASED, /* void **/ _VG_USERREQ__HG_POSIX_SEM_ACQUIRED, /* void **/ _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong, ulong */ _VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, /* pth_bar_t* */ _VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, /* pth_bar_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, /* pth_slk_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST, /* pth_slk_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, /* pth_slk_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, /* pth_slk_t* */ _VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE, /* pth_slk_t* */ _VG_USERREQ__HG_CLIENTREQ_UNIMP, /* char* */ _VG_USERREQ__HG_USERSO_SEND_PRE, /* arbitrary UWord SO-tag */ _VG_USERREQ__HG_USERSO_RECV_POST, /* arbitrary UWord SO-tag */ _VG_USERREQ__HG_USERSO_FORGET_ALL, /* arbitrary UWord SO-tag */ _VG_USERREQ__HG_RESERVED2, /* Do not use */ _VG_USERREQ__HG_RESERVED3, /* Do not use */ _VG_USERREQ__HG_RESERVED4, /* Do not use */ _VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, /* Addr a, ulong len */ _VG_USERREQ__HG_ARANGE_MAKE_TRACKED, /* Addr a, ulong len */ _VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, /* pth_bar_t*, ulong */ _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, /* Addr start_of_block */ _VG_USERREQ__HG_PTHREAD_COND_INIT_POST, /* pth_cond_t*, pth_cond_attr_t*/ _VG_USERREQ__HG_GNAT_MASTER_HOOK, /* void*d,void*m,Word ml */ _VG_USERREQ__HG_GNAT_MASTER_COMPLETED_HOOK, /* void*s,Word ml */ _VG_USERREQ__HG_GET_ABITS, /* Addr a,Addr abits, ulong len */ _VG_USERREQ__HG_PTHREAD_CREATE_BEGIN, _VG_USERREQ__HG_PTHREAD_CREATE_END, _VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE, /* pth_mx_t*,long isTryLock */ _VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST, /* pth_mx_t *,long tookLock */ _VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, /* pth_rwlk_t*,long isW,long */ _VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, /* pth_rwlk_t* */ _VG_USERREQ__HG_POSIX_SEM_POST_PRE, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_POST_POST, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_WAIT_PRE, /* sem_t* */ _VG_USERREQ__HG_POSIX_SEM_WAIT_POST, /* sem_t*, long tookLock */ _VG_USERREQ__HG_PTHREAD_COND_SIGNAL_POST, /* pth_cond_t* */ _VG_USERREQ__HG_PTHREAD_COND_BROADCAST_POST,/* pth_cond_t* */ _VG_USERREQ__HG_RTLD_BIND_GUARD, /* int flags */ _VG_USERREQ__HG_RTLD_BIND_CLEAR, /* int flags */ _VG_USERREQ__HG_GNAT_DEPENDENT_MASTER_JOIN /* void*d, void*m */ } Vg_TCheckClientRequest; /*----------------------------------------------------------------*/ /*--- ---*/ /*--- Implementation-only facilities. Not for end-user use. ---*/ /*--- For end-user facilities see below (the next section in ---*/ /*--- this file.) ---*/ /*--- ---*/ /*----------------------------------------------------------------*/ /* Do a client request. These are macros rather than a functions so as to avoid having an extra frame in stack traces. NB: these duplicate definitions in hg_intercepts.c. But here, we have to make do with weaker typing (no definition of Word etc) and no assertions, whereas in helgrind.h we can use those facilities. Obviously it's important the two sets of definitions are kept in sync. The commented-out asserts should actually hold, but unfortunately they can't be allowed to be visible here, because that would require the end-user code to #include . */ #define DO_CREQ_v_W(_creqF, _ty1F,_arg1F) \ do { \ long int _arg1; \ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ _arg1 = (long int)(_arg1F); \ VALGRIND_DO_CLIENT_REQUEST_STMT( \ (_creqF), \ _arg1, 0,0,0,0); \ } while (0) #define DO_CREQ_W_W(_resF, _dfltF, _creqF, _ty1F,_arg1F) \ do { \ long int _arg1; \ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ _arg1 = (long int)(_arg1F); \ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR( \ (_dfltF), \ (_creqF), \ _arg1, 0,0,0,0); \ _resF = _qzz_res; \ } while (0) #define DO_CREQ_v_WW(_creqF, _ty1F,_arg1F, _ty2F,_arg2F) \ do { \ long int _arg1, _arg2; \ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ /* assert(sizeof(_ty2F) == sizeof(long int)); */ \ _arg1 = (long int)(_arg1F); \ _arg2 = (long int)(_arg2F); \ VALGRIND_DO_CLIENT_REQUEST_STMT( \ (_creqF), \ _arg1,_arg2,0,0,0); \ } while (0) #define DO_CREQ_v_WWW(_creqF, _ty1F,_arg1F, \ _ty2F,_arg2F, _ty3F, _arg3F) \ do { \ long int _arg1, _arg2, _arg3; \ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ /* assert(sizeof(_ty2F) == sizeof(long int)); */ \ /* assert(sizeof(_ty3F) == sizeof(long int)); */ \ _arg1 = (long int)(_arg1F); \ _arg2 = (long int)(_arg2F); \ _arg3 = (long int)(_arg3F); \ VALGRIND_DO_CLIENT_REQUEST_STMT( \ (_creqF), \ _arg1,_arg2,_arg3,0,0); \ } while (0) #define DO_CREQ_W_WWW(_resF, _dfltF, _creqF, _ty1F,_arg1F, \ _ty2F,_arg2F, _ty3F, _arg3F) \ do { \ long int _qzz_res; \ long int _arg1, _arg2, _arg3; \ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \ _arg1 = (long int)(_arg1F); \ _arg2 = (long int)(_arg2F); \ _arg3 = (long int)(_arg3F); \ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR( \ (_dfltF), \ (_creqF), \ _arg1,_arg2,_arg3,0,0); \ _resF = _qzz_res; \ } while (0) #define _HG_CLIENTREQ_UNIMP(_qzz_str) \ DO_CREQ_v_W(_VG_USERREQ__HG_CLIENTREQ_UNIMP, \ (char*),(_qzz_str)) /*----------------------------------------------------------------*/ /*--- ---*/ /*--- Helgrind-native requests. These allow access to ---*/ /*--- the same set of annotation primitives that are used ---*/ /*--- to build the POSIX pthread wrappers. ---*/ /*--- ---*/ /*----------------------------------------------------------------*/ /* ---------------------------------------------------------- For describing ordinary mutexes (non-rwlocks). For rwlock descriptions see ANNOTATE_RWLOCK_* below. ---------------------------------------------------------- */ /* Notify here immediately after mutex creation. _mbRec == 0 for a non-recursive mutex, 1 for a recursive mutex. */ #define VALGRIND_HG_MUTEX_INIT_POST(_mutex, _mbRec) \ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, \ void*,(_mutex), long,(_mbRec)) /* Notify here immediately before mutex acquisition. _isTryLock == 0 for a normal acquisition, 1 for a "try" style acquisition. */ #define VALGRIND_HG_MUTEX_LOCK_PRE(_mutex, _isTryLock) \ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_PRE, \ void*,(_mutex), long,(_isTryLock)) /* Notify here immediately after a successful mutex acquisition. */ #define VALGRIND_HG_MUTEX_LOCK_POST(_mutex) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_ACQUIRE_POST, \ void*,(_mutex)) /* Notify here immediately before a mutex release. */ #define VALGRIND_HG_MUTEX_UNLOCK_PRE(_mutex) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, \ void*,(_mutex)) /* Notify here immediately after a mutex release. */ #define VALGRIND_HG_MUTEX_UNLOCK_POST(_mutex) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, \ void*,(_mutex)) /* Notify here immediately before mutex destruction. */ #define VALGRIND_HG_MUTEX_DESTROY_PRE(_mutex) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, \ void*,(_mutex)) /* ---------------------------------------------------------- For describing semaphores. ---------------------------------------------------------- */ /* Notify here immediately after semaphore creation. */ #define VALGRIND_HG_SEM_INIT_POST(_sem, _value) \ DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST, \ void*, (_sem), unsigned long, (_value)) /* Notify here immediately after a semaphore wait (an acquire-style operation) */ #define VALGRIND_HG_SEM_WAIT_POST(_sem) \ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_ACQUIRED, \ void*,(_sem)) /* Notify here immediately before semaphore post (a release-style operation) */ #define VALGRIND_HG_SEM_POST_PRE(_sem) \ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_RELEASED, \ void*,(_sem)) /* Notify here immediately before semaphore destruction. */ #define VALGRIND_HG_SEM_DESTROY_PRE(_sem) \ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, \ void*, (_sem)) /* ---------------------------------------------------------- For describing barriers. ---------------------------------------------------------- */ /* Notify here immediately before barrier creation. _count is the capacity. _resizable == 0 means the barrier may not be resized, 1 means it may be. */ #define VALGRIND_HG_BARRIER_INIT_PRE(_bar, _count, _resizable) \ DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, \ void*,(_bar), \ unsigned long,(_count), \ unsigned long,(_resizable)) /* Notify here immediately before arrival at a barrier. */ #define VALGRIND_HG_BARRIER_WAIT_PRE(_bar) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, \ void*,(_bar)) /* Notify here immediately before a resize (change of barrier capacity). If _newcount >= the existing capacity, then there is no change in the state of any threads waiting at the barrier. If _newcount < the existing capacity, and >= _newcount threads are currently waiting at the barrier, then this notification is considered to also have the effect of telling the checker that all waiting threads have now moved past the barrier. (I can't think of any other sane semantics.) */ #define VALGRIND_HG_BARRIER_RESIZE_PRE(_bar, _newcount) \ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, \ void*,(_bar), \ unsigned long,(_newcount)) /* Notify here immediately before barrier destruction. */ #define VALGRIND_HG_BARRIER_DESTROY_PRE(_bar) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, \ void*,(_bar)) /* ---------------------------------------------------------- For describing memory ownership changes. ---------------------------------------------------------- */ /* Clean memory state. This makes Helgrind forget everything it knew about the specified memory range. Effectively this announces that the specified memory range now "belongs" to the calling thread, so that: (1) the calling thread can access it safely without synchronisation, and (2) all other threads must sync with this one to access it safely. This is particularly useful for memory allocators that wish to recycle memory. */ #define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \ DO_CREQ_v_WW(VG_USERREQ__HG_CLEAN_MEMORY, \ void*,(_qzz_start), \ unsigned long,(_qzz_len)) /* The same, but for the heap block starting at _qzz_blockstart. This allows painting when we only know the address of an object, but not its size, which is sometimes the case in C++ code involving inheritance, and in which RTTI is not, for whatever reason, available. Returns the number of bytes painted, which can be zero for a zero-sized block. Hence, return values >= 0 indicate success (the block was found), and the value -1 indicates block not found, and -2 is returned when not running on Helgrind. */ #define VALGRIND_HG_CLEAN_MEMORY_HEAPBLOCK(_qzz_blockstart) \ (__extension__ \ ({long int _npainted; \ DO_CREQ_W_W(_npainted, (-2)/*default*/, \ _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, \ void*,(_qzz_blockstart)); \ _npainted; \ })) /* ---------------------------------------------------------- For error control. ---------------------------------------------------------- */ /* Tell H that an address range is not to be "tracked" until further notice. This puts it in the NOACCESS state, in which case we ignore all reads and writes to it. Useful for ignoring ranges of memory where there might be races we don't want to see. If the memory is subsequently reallocated via malloc/new/stack allocation, then it is put back in the trackable state. Hence it is safe in the situation where checking is disabled, the containing area is deallocated and later reallocated for some other purpose. */ #define VALGRIND_HG_DISABLE_CHECKING(_qzz_start, _qzz_len) \ DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, \ void*,(_qzz_start), \ unsigned long,(_qzz_len)) /* And put it back into the normal "tracked" state, that is, make it once again subject to the normal race-checking machinery. This puts it in the same state as new memory allocated by this thread -- that is, basically owned exclusively by this thread. */ #define VALGRIND_HG_ENABLE_CHECKING(_qzz_start, _qzz_len) \ DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_TRACKED, \ void*,(_qzz_start), \ unsigned long,(_qzz_len)) /* Checks the accessibility bits for addresses [zza..zza+zznbytes-1]. If zzabits array is provided, copy the accessibility bits in zzabits. Return values: -2 if not running on helgrind -1 if any parts of zzabits is not addressable >= 0 : success. When success, it returns the nr of addressable bytes found. So, to check that a whole range is addressable, check VALGRIND_HG_GET_ABITS(addr,NULL,len) == len In addition, if you want to examine the addressability of each byte of the range, you need to provide a non NULL ptr as second argument, pointing to an array of unsigned char of length len. Addressable bytes are indicated with 0xff. Non-addressable bytes are indicated with 0x00. */ #define VALGRIND_HG_GET_ABITS(zza,zzabits,zznbytes) \ (__extension__ \ ({long int _res; \ DO_CREQ_W_WWW(_res, (-2)/*default*/, \ _VG_USERREQ__HG_GET_ABITS, \ void*,(zza), void*,(zzabits), \ unsigned long,(zznbytes)); \ _res; \ })) /* End-user request for Ada applications compiled with GNAT. Helgrind understands the Ada concept of Ada task dependencies and terminations. See Ada Reference Manual section 9.3 "Task Dependence - Termination of Tasks". However, in some cases, the master of (terminated) tasks completes only when the application exits. An example of this is dynamically allocated tasks with an access type defined at Library Level. By default, the state of such tasks in Helgrind will be 'exited but join not done yet'. Many tasks in such a state are however causing Helgrind CPU and memory to increase significantly. VALGRIND_HG_GNAT_DEPENDENT_MASTER_JOIN can be used to indicate to Helgrind that a not yet completed master has however already 'seen' the termination of a dependent : this is conceptually the same as a pthread_join and causes the cleanup of the dependent as done by Helgrind when a master completes. This allows to avoid the overhead in helgrind caused by such tasks. A typical usage for a master to indicate it has done conceptually a join with a dependent task before the master completes is: while not Dep_Task'Terminated loop ... do whatever to wait for Dep_Task termination. end loop; VALGRIND_HG_GNAT_DEPENDENT_MASTER_JOIN (Dep_Task'Identity, Ada.Task_Identification.Current_Task); Note that VALGRIND_HG_GNAT_DEPENDENT_MASTER_JOIN should be a binding to a C function built with the below macro. */ #define VALGRIND_HG_GNAT_DEPENDENT_MASTER_JOIN(_qzz_dep, _qzz_master) \ DO_CREQ_v_WW(_VG_USERREQ__HG_GNAT_DEPENDENT_MASTER_JOIN, \ void*,(_qzz_dep), \ void*,(_qzz_master)) /*----------------------------------------------------------------*/ /*--- ---*/ /*--- ThreadSanitizer-compatible requests ---*/ /*--- (mostly unimplemented) ---*/ /*--- ---*/ /*----------------------------------------------------------------*/ /* A quite-broad set of annotations, as used in the ThreadSanitizer project. This implementation aims to be a (source-level) compatible implementation of the macros defined in: http://code.google.com/p/data-race-test/source /browse/trunk/dynamic_annotations/dynamic_annotations.h (some of the comments below are taken from the above file) The implementation here is very incomplete, and intended as a starting point. Many of the macros are unimplemented. Rather than allowing unimplemented macros to silently do nothing, they cause an assertion. Intention is to implement them on demand. The major use of these macros is to make visible to race detectors, the behaviour (effects) of user-implemented synchronisation primitives, that the detectors could not otherwise deduce from the normal observation of pthread etc calls. Some of the macros are no-ops in Helgrind. That's because Helgrind is a pure happens-before detector, whereas ThreadSanitizer uses a hybrid lockset and happens-before scheme, which requires more accurate annotations for correct operation. The macros are listed in the same order as in dynamic_annotations.h (URL just above). I should point out that I am less than clear about the intended semantics of quite a number of them. Comments and clarifications welcomed! */ /* ---------------------------------------------------------------- These four allow description of user-level condition variables, apparently in the style of POSIX's pthread_cond_t. Currently unimplemented and will assert. ---------------------------------------------------------------- */ /* Report that wait on the condition variable at address CV has succeeded and the lock at address LOCK is now held. CV and LOCK are completely arbitrary memory addresses which presumably mean something to the application, but are meaningless to Helgrind. */ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_LOCK_WAIT") /* Report that wait on the condition variable at CV has succeeded. Variant w/o lock. */ #define ANNOTATE_CONDVAR_WAIT(cv) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_WAIT") /* Report that we are about to signal on the condition variable at address CV. */ #define ANNOTATE_CONDVAR_SIGNAL(cv) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL") /* Report that we are about to signal_all on the condition variable at CV. */ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL_ALL") /* ---------------------------------------------------------------- Create completely arbitrary happens-before edges between threads. If threads T1 .. Tn all do ANNOTATE_HAPPENS_BEFORE(obj) and later (w.r.t. some notional global clock for the computation) thread Tm does ANNOTATE_HAPPENS_AFTER(obj), then Helgrind will regard all memory accesses done by T1 .. Tn before the ..BEFORE.. call as happening-before all memory accesses done by Tm after the ..AFTER.. call. Hence Helgrind won't complain about races if Tm's accesses afterwards are to the same locations as accesses before by any of T1 .. Tn. OBJ is a machine word (unsigned long, or void*), is completely arbitrary, and denotes the identity of some synchronisation object you're modelling. You must do the _BEFORE call just before the real sync event on the signaller's side, and _AFTER just after the real sync event on the waiter's side. If none of the rest of these macros make sense to you, at least take the time to understand these two. They form the very essence of describing arbitrary inter-thread synchronisation events to Helgrind. You can get a long way just with them alone. See also, extensive discussion on semantics of this in https://bugs.kde.org/show_bug.cgi?id=243935 ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) is interim until such time as bug 243935 is fully resolved. It instructs Helgrind to forget about any ANNOTATE_HAPPENS_BEFORE calls on the specified object, in effect putting it back in its original state. Once in that state, a use of ANNOTATE_HAPPENS_AFTER on it has no effect on the calling thread. An implementation may optionally release resources it has associated with 'obj' when ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) happens. Users are recommended to use ANNOTATE_HAPPENS_BEFORE_FORGET_ALL to indicate when a synchronisation object is no longer needed, so as to avoid potential indefinite resource leaks. ---------------------------------------------------------------- */ #define ANNOTATE_HAPPENS_BEFORE(obj) \ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_SEND_PRE, void*,(obj)) #define ANNOTATE_HAPPENS_AFTER(obj) \ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_RECV_POST, void*,(obj)) #define ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) \ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_FORGET_ALL, void*,(obj)) /* ---------------------------------------------------------------- Memory publishing. The TSan sources say: Report that the bytes in the range [pointer, pointer+size) are about to be published safely. The race checker will create a happens-before arc from the call ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) to subsequent accesses to this memory. I'm not sure I understand what this means exactly, nor whether it is relevant for a pure h-b detector. Leaving unimplemented for now. ---------------------------------------------------------------- */ #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PUBLISH_MEMORY_RANGE") /* DEPRECATED. Don't use it. */ /* #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) */ /* DEPRECATED. Don't use it. */ /* #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) */ /* ---------------------------------------------------------------- TSan sources say: Instruct the tool to create a happens-before arc between MU->Unlock() and MU->Lock(). This annotation may slow down the race detector; normally it is used only when it would be difficult to annotate each of the mutex's critical sections individually using the annotations above. If MU is a posix pthread_mutex_t then Helgrind will do this anyway. In any case, leave as unimp for now. I'm unsure about the intended behaviour. ---------------------------------------------------------------- */ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX") /* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */ /* #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) */ /* ---------------------------------------------------------------- TSan sources say: Annotations useful when defining memory allocators, or when memory that was protected in one way starts to be protected in another. Report that a new memory at "address" of size "size" has been allocated. This might be used when the memory has been retrieved from a free list and is about to be reused, or when a the locking discipline for a variable changes. AFAICS this is the same as VALGRIND_HG_CLEAN_MEMORY. ---------------------------------------------------------------- */ #define ANNOTATE_NEW_MEMORY(address, size) \ VALGRIND_HG_CLEAN_MEMORY((address), (size)) /* ---------------------------------------------------------------- TSan sources say: Annotations useful when defining FIFO queues that transfer data between threads. All unimplemented. Am not claiming to understand this (yet). ---------------------------------------------------------------- */ /* Report that the producer-consumer queue object at address PCQ has been created. The ANNOTATE_PCQ_* annotations should be used only for FIFO queues. For non-FIFO queues use ANNOTATE_HAPPENS_BEFORE (for put) and ANNOTATE_HAPPENS_AFTER (for get). */ #define ANNOTATE_PCQ_CREATE(pcq) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_CREATE") /* Report that the queue at address PCQ is about to be destroyed. */ #define ANNOTATE_PCQ_DESTROY(pcq) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_DESTROY") /* Report that we are about to put an element into a FIFO queue at address PCQ. */ #define ANNOTATE_PCQ_PUT(pcq) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_PUT") /* Report that we've just got an element from a FIFO queue at address PCQ. */ #define ANNOTATE_PCQ_GET(pcq) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_GET") /* ---------------------------------------------------------------- Annotations that suppress errors. It is usually better to express the program's synchronization using the other annotations, but these can be used when all else fails. Currently these are all unimplemented. I can't think of a simple way to implement them without at least some performance overhead. ---------------------------------------------------------------- */ /* Report that we may have a benign race at "pointer", with size "sizeof(*(pointer))". "pointer" must be a non-void *pointer. Insert at the point where "pointer" has been allocated, preferably close to the point where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. XXX: what's this actually supposed to do? And what's the type of DESCRIPTION? When does the annotation stop having an effect? */ #define ANNOTATE_BENIGN_RACE(pointer, description) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BENIGN_RACE") /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to the memory range [address, address+size). */ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ VALGRIND_HG_DISABLE_CHECKING(address, size) /* Request the analysis tool to ignore all reads in the current thread until ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey reads, while still checking other reads and all writes. */ #define ANNOTATE_IGNORE_READS_BEGIN() \ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_BEGIN") /* Stop ignoring reads. */ #define ANNOTATE_IGNORE_READS_END() \ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_END") /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */ #define ANNOTATE_IGNORE_WRITES_BEGIN() \ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_BEGIN") /* Stop ignoring writes. */ #define ANNOTATE_IGNORE_WRITES_END() \ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_END") /* Start ignoring all memory accesses (reads and writes). */ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ do { \ ANNOTATE_IGNORE_READS_BEGIN(); \ ANNOTATE_IGNORE_WRITES_BEGIN(); \ } while (0) /* Stop ignoring all memory accesses. */ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ do { \ ANNOTATE_IGNORE_WRITES_END(); \ ANNOTATE_IGNORE_READS_END(); \ } while (0) /* ---------------------------------------------------------------- Annotations useful for debugging. Again, so for unimplemented, partly for performance reasons. ---------------------------------------------------------------- */ /* Request to trace every access to ADDRESS. */ #define ANNOTATE_TRACE_MEMORY(address) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_TRACE_MEMORY") /* Report the current thread name to a race detector. */ #define ANNOTATE_THREAD_NAME(name) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_THREAD_NAME") /* ---------------------------------------------------------------- Annotations for describing behaviour of user-implemented lock primitives. In all cases, the LOCK argument is a completely arbitrary machine word (unsigned long, or void*) and can be any value which gives a unique identity to the lock objects being modelled. We just pretend they're ordinary posix rwlocks. That'll probably give some rather confusing wording in error messages, claiming that the arbitrary LOCK values are pthread_rwlock_t*'s, when in fact they are not. Ah well. ---------------------------------------------------------------- */ /* Report that a lock has just been created at address LOCK. */ #define ANNOTATE_RWLOCK_CREATE(lock) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, \ void*,(lock)) /* Report that the lock at address LOCK is about to be destroyed. */ #define ANNOTATE_RWLOCK_DESTROY(lock) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, \ void*,(lock)) /* Report that the lock at address LOCK has just been acquired. is_w=1 for writer lock, is_w=0 for reader lock. */ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_RWLOCK_ACQUIRED, \ void*,(lock), unsigned long,(is_w)) /* Report that the lock at address LOCK is about to be released. */ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_RELEASED, \ void*,(lock)) /* is_w is ignored */ /* ------------------------------------------------------------- Annotations useful when implementing barriers. They are not normally needed by modules that merely use barriers. The "barrier" argument is a pointer to the barrier object. ---------------------------------------------------------------- */ /* Report that the "barrier" has been initialized with initial "count". If 'reinitialization_allowed' is true, initialization is allowed to happen multiple times w/o calling barrier_destroy() */ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_INIT") /* Report that we are about to enter barrier_wait("barrier"). */ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") /* Report that we just exited barrier_wait("barrier"). */ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") /* Report that the "barrier" has been destroyed. */ #define ANNOTATE_BARRIER_DESTROY(barrier) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY") /* ---------------------------------------------------------------- Annotations useful for testing race detectors. ---------------------------------------------------------------- */ /* Report that we expect a race on the variable at ADDRESS. Use only in unit tests for a race detector. */ #define ANNOTATE_EXPECT_RACE(address, description) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_EXPECT_RACE") /* A no-op. Insert where you like to test the interceptors. */ #define ANNOTATE_NO_OP(arg) \ _HG_CLIENTREQ_UNIMP("ANNOTATE_NO_OP") /* Force the race detector to flush its state. The actual effect depends on * the implementation of the detector. */ #define ANNOTATE_FLUSH_STATE() \ _HG_CLIENTREQ_UNIMP("ANNOTATE_FLUSH_STATE") #endif /* __HELGRIND_H */ pmemkv-1.1/src/valgrind/memcheck.h000066400000000000000000000343521361504041500172020ustar00rootroot00000000000000 /* ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (memcheck.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 MemCheck, a heavyweight Valgrind tool for detecting memory errors. Copyright (C) 2000-2017 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 (memcheck.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. ---------------------------------------------------------------- */ #ifndef __MEMCHECK_H #define __MEMCHECK_H /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query memory permissions inside your own programs. See comment near the top of valgrind.h on how to use them. */ #include "valgrind.h" /* !! 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__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'), VG_USERREQ__MAKE_MEM_UNDEFINED, VG_USERREQ__MAKE_MEM_DEFINED, VG_USERREQ__DISCARD, VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, VG_USERREQ__CHECK_MEM_IS_DEFINED, VG_USERREQ__DO_LEAK_CHECK, VG_USERREQ__COUNT_LEAKS, VG_USERREQ__GET_VBITS, VG_USERREQ__SET_VBITS, VG_USERREQ__CREATE_BLOCK, VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, /* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */ VG_USERREQ__COUNT_LEAK_BLOCKS, VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE, VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE, /* This is just for memcheck's internal use - don't use it */ _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR = VG_USERREQ_TOOL_BASE('M','C') + 256 } Vg_MemCheckClientRequest; /* Client-code macros to manipulate the state of memory. */ /* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */ #define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__MAKE_MEM_NOACCESS, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Similarly, mark memory at _qzz_addr as addressable but undefined for _qzz_len bytes. */ #define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__MAKE_MEM_UNDEFINED, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Similarly, mark memory at _qzz_addr as addressable and defined for _qzz_len bytes. */ #define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__MAKE_MEM_DEFINED, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is not altered: bytes which are addressable are marked as defined, but those which are not addressable are left unchanged. */ #define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Create a block-description handle. The description is an ascii string which is included in any messages pertaining to addresses within the specified memory range. Has no other effect on the properties of the memory range. */ #define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CREATE_BLOCK, \ (_qzz_addr), (_qzz_len), (_qzz_desc), \ 0, 0) /* Discard a block-description-handle. Returns 1 for an invalid handle, 0 for a valid handle. */ #define VALGRIND_DISCARD(_qzz_blkindex) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__DISCARD, \ 0, (_qzz_blkindex), 0, 0, 0) /* Client-code macros to check the state of memory. */ /* Check that memory at _qzz_addr is addressable for _qzz_len bytes. If suitable addressibility is not established, Valgrind prints an error message and returns the address of the first offending byte. Otherwise it returns zero. */ #define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Check that memory at _qzz_addr is addressable and defined for _qzz_len bytes. If suitable addressibility and definedness are not established, Valgrind prints an error message and returns the address of the first offending byte. Otherwise it returns zero. */ #define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__CHECK_MEM_IS_DEFINED, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /* Use this macro to force the definedness and addressibility of an lvalue to be checked. If suitable addressibility and definedness are not established, Valgrind prints an error message and returns the address of the first offending byte. Otherwise it returns zero. */ #define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \ VALGRIND_CHECK_MEM_IS_DEFINED( \ (volatile unsigned char *)&(__lvalue), \ (unsigned long)(sizeof (__lvalue))) /* Do a full memory leak check (like --leak-check=full) mid-execution. */ #define VALGRIND_DO_LEAK_CHECK \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ 0, 0, 0, 0, 0) /* Same as VALGRIND_DO_LEAK_CHECK but only showing the entries for which there was an increase in leaked bytes or leaked nr of blocks since the previous leak search. */ #define VALGRIND_DO_ADDED_LEAK_CHECK \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ 0, 1, 0, 0, 0) /* Same as VALGRIND_DO_ADDED_LEAK_CHECK but showing entries with increased or decreased leaked bytes/blocks since previous leak search. */ #define VALGRIND_DO_CHANGED_LEAK_CHECK \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ 0, 2, 0, 0, 0) /* Do a summary memory leak check (like --leak-check=summary) mid-execution. */ #define VALGRIND_DO_QUICK_LEAK_CHECK \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ 1, 0, 0, 0, 0) /* Return number of leaked, dubious, reachable and suppressed bytes found by all previous leak checks. They must be lvalues. */ #define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \ /* For safety on 64-bit platforms we assign the results to private unsigned long variables, then assign these to the lvalues the user specified, which works no matter what type 'leaked', 'dubious', etc are. We also initialise '_qzz_leaked', etc because VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as defined. */ \ { \ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \ VALGRIND_DO_CLIENT_REQUEST_STMT( \ VG_USERREQ__COUNT_LEAKS, \ &_qzz_leaked, &_qzz_dubious, \ &_qzz_reachable, &_qzz_suppressed, 0); \ leaked = _qzz_leaked; \ dubious = _qzz_dubious; \ reachable = _qzz_reachable; \ suppressed = _qzz_suppressed; \ } /* Return number of leaked, dubious, reachable and suppressed bytes found by all previous leak checks. They must be lvalues. */ #define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \ /* For safety on 64-bit platforms we assign the results to private unsigned long variables, then assign these to the lvalues the user specified, which works no matter what type 'leaked', 'dubious', etc are. We also initialise '_qzz_leaked', etc because VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as defined. */ \ { \ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \ VALGRIND_DO_CLIENT_REQUEST_STMT( \ VG_USERREQ__COUNT_LEAK_BLOCKS, \ &_qzz_leaked, &_qzz_dubious, \ &_qzz_reachable, &_qzz_suppressed, 0); \ leaked = _qzz_leaked; \ dubious = _qzz_dubious; \ reachable = _qzz_reachable; \ suppressed = _qzz_suppressed; \ } /* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it into the provided zzvbits array. Return values: 0 if not running on valgrind 1 success 2 [previously indicated unaligned arrays; these are now allowed] 3 if any parts of zzsrc/zzvbits are not addressable. The metadata is not copied in cases 0, 2 or 3 so it should be impossible to segfault your system by using this call. */ #define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__GET_VBITS, \ (const char*)(zza), \ (char*)(zzvbits), \ (zznbytes), 0, 0) /* Set the validity data for addresses [zza..zza+zznbytes-1], copying it from the provided zzvbits array. Return values: 0 if not running on valgrind 1 success 2 [previously indicated unaligned arrays; these are now allowed] 3 if any parts of zza/zzvbits are not addressable. The metadata is not copied in cases 0, 2 or 3 so it should be impossible to segfault your system by using this call. */ #define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__SET_VBITS, \ (const char*)(zza), \ (const char*)(zzvbits), \ (zznbytes), 0, 0 ) /* Disable and re-enable reporting of addressing errors in the specified address range. */ #define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE, \ (_qzz_addr), (_qzz_len), 0, 0, 0) #define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE, \ (_qzz_addr), (_qzz_len), 0, 0, 0) #endif pmemkv-1.1/src/valgrind/pmemcheck.h000066400000000000000000000245511361504041500173620ustar00rootroot00000000000000/* * Copyright (c) 2014-2015, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef __PMEMCHECK_H #define __PMEMCHECK_H /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query memory permissions inside your own programs. See comment near the top of valgrind.h on how to use them. */ #include "valgrind.h" /* !! 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__PMC_REGISTER_PMEM_MAPPING = VG_USERREQ_TOOL_BASE('P','C'), VG_USERREQ__PMC_REGISTER_PMEM_FILE, VG_USERREQ__PMC_REMOVE_PMEM_MAPPING, VG_USERREQ__PMC_CHECK_IS_PMEM_MAPPING, VG_USERREQ__PMC_PRINT_PMEM_MAPPINGS, VG_USERREQ__PMC_DO_FLUSH, VG_USERREQ__PMC_DO_FENCE, VG_USERREQ__PMC_RESERVED1, /* Do not use. */ VG_USERREQ__PMC_WRITE_STATS, VG_USERREQ__PMC_RESERVED2, /* Do not use. */ VG_USERREQ__PMC_RESERVED3, /* Do not use. */ VG_USERREQ__PMC_RESERVED4, /* Do not use. */ VG_USERREQ__PMC_RESERVED5, /* Do not use. */ VG_USERREQ__PMC_RESERVED7, /* Do not use. */ VG_USERREQ__PMC_RESERVED8, /* Do not use. */ VG_USERREQ__PMC_RESERVED9, /* Do not use. */ VG_USERREQ__PMC_RESERVED10, /* Do not use. */ VG_USERREQ__PMC_SET_CLEAN, /* transaction support */ VG_USERREQ__PMC_START_TX, VG_USERREQ__PMC_START_TX_N, VG_USERREQ__PMC_END_TX, VG_USERREQ__PMC_END_TX_N, VG_USERREQ__PMC_ADD_TO_TX, VG_USERREQ__PMC_ADD_TO_TX_N, VG_USERREQ__PMC_REMOVE_FROM_TX, VG_USERREQ__PMC_REMOVE_FROM_TX_N, VG_USERREQ__PMC_ADD_THREAD_TO_TX_N, VG_USERREQ__PMC_REMOVE_THREAD_FROM_TX_N, VG_USERREQ__PMC_ADD_TO_GLOBAL_TX_IGNORE, VG_USERREQ__PMC_RESERVED6, /* Do not use. */ VG_USERREQ__PMC_EMIT_LOG, } Vg_PMemCheckClientRequest; /* Client-code macros to manipulate pmem mappings */ /** Register a persistent memory mapping region */ #define VALGRIND_PMC_REGISTER_PMEM_MAPPING(_qzz_addr, _qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REGISTER_PMEM_MAPPING, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Register a persistent memory file */ #define VALGRIND_PMC_REGISTER_PMEM_FILE(_qzz_desc, _qzz_addr_base, \ _qzz_size, _qzz_offset) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REGISTER_PMEM_FILE, \ (_qzz_desc), (_qzz_addr_base), (_qzz_size), \ (_qzz_offset), 0) /** Remove a persistent memory mapping region */ #define VALGRIND_PMC_REMOVE_PMEM_MAPPING(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REMOVE_PMEM_MAPPING, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Check if the given range is a registered persistent memory mapping */ #define VALGRIND_PMC_CHECK_IS_PMEM_MAPPING(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_CHECK_IS_PMEM_MAPPING, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Register an SFENCE */ #define VALGRIND_PMC_PRINT_PMEM_MAPPINGS \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_PRINT_PMEM_MAPPINGS, \ 0, 0, 0, 0, 0) /** Register a CLFLUSH-like operation */ #define VALGRIND_PMC_DO_FLUSH(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_DO_FLUSH, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Register an SFENCE */ #define VALGRIND_PMC_DO_FENCE \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_DO_FENCE, \ 0, 0, 0, 0, 0) /** Write tool stats */ #define VALGRIND_PMC_WRITE_STATS \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_WRITE_STATS, \ 0, 0, 0, 0, 0) /** Emit user log */ #define VALGRIND_PMC_EMIT_LOG(_qzz_emit_log) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_EMIT_LOG, \ (_qzz_emit_log), 0, 0, 0, 0) /** Set a region of persistent memory as clean */ #define VALGRIND_PMC_SET_CLEAN(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_SET_CLEAN, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Support for transactions */ /** Start an implicit persistent memory transaction */ #define VALGRIND_PMC_START_TX \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_START_TX, \ 0, 0, 0, 0, 0) /** Start an explicit persistent memory transaction */ #define VALGRIND_PMC_START_TX_N(_qzz_txn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_START_TX_N, \ (_qzz_txn), 0, 0, 0, 0) /** End an implicit persistent memory transaction */ #define VALGRIND_PMC_END_TX \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_END_TX, \ 0, 0, 0, 0, 0) /** End an explicit persistent memory transaction */ #define VALGRIND_PMC_END_TX_N(_qzz_txn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_END_TX_N, \ (_qzz_txn), 0, 0, 0, 0) /** Add a persistent memory region to the implicit transaction */ #define VALGRIND_PMC_ADD_TO_TX(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_ADD_TO_TX, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Add a persistent memory region to an explicit transaction */ #define VALGRIND_PMC_ADD_TO_TX_N(_qzz_txn,_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_ADD_TO_TX_N, \ (_qzz_txn), (_qzz_addr), (_qzz_len), 0, 0) /** Remove a persistent memory region from the implicit transaction */ #define VALGRIND_PMC_REMOVE_FROM_TX(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REMOVE_FROM_TX, \ (_qzz_addr), (_qzz_len), 0, 0, 0) /** Remove a persistent memory region from an explicit transaction */ #define VALGRIND_PMC_REMOVE_FROM_TX_N(_qzz_txn,_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REMOVE_FROM_TX_N, \ (_qzz_txn), (_qzz_addr), (_qzz_len), 0, 0) /** End an explicit persistent memory transaction */ #define VALGRIND_PMC_ADD_THREAD_TX_N(_qzz_txn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_ADD_THREAD_TO_TX_N, \ (_qzz_txn), 0, 0, 0, 0) /** End an explicit persistent memory transaction */ #define VALGRIND_PMC_REMOVE_THREAD_FROM_TX_N(_qzz_txn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__PMC_REMOVE_THREAD_FROM_TX_N, \ (_qzz_txn), 0, 0, 0, 0) /** Remove a persistent memory region from the implicit transaction */ #define VALGRIND_PMC_ADD_TO_GLOBAL_TX_IGNORE(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PMC_ADD_TO_GLOBAL_TX_IGNORE,\ (_qzz_addr), (_qzz_len), 0, 0, 0) #endif pmemkv-1.1/src/valgrind/valgrind.h000066400000000000000000013752211361504041500172400ustar00rootroot00000000000000/* -*- 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-2017 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__ 14 #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 #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #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__) && !defined(__ILP32__) # 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 #elif defined(__sun) && defined(__i386__) # define PLAT_x86_solaris 1 #elif defined(__sun) && defined(__x86_64__) # define PLAT_amd64_solaris 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,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || (defined(PLAT_x86_win32) && defined(__GNUC__)) \ || defined(PLAT_x86_solaris) 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__) || PLAT_x86_solaris */ /* ------------------------- 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,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) \ || (defined(PLAT_amd64_win64) && defined(__GNUC__)) typedef struct { unsigned 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 int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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 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 || PLAT_amd64_solaris */ /* ------------------------- 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 int nraddr; /* where's the code? */ unsigned 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 int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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 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 int nraddr; /* where's the code? */ unsigned 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 int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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 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 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 int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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" ((unsigned long int)(_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 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 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 int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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 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", "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 \ /* %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 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 int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned 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", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned 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,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || defined(PLAT_x86_solaris) /* 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 || PLAT_x86_solaris */ /* ---------------- amd64-{linux,darwin,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) /* 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 || PLAT_amd64_solaris */ /* ------------------------ 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", "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", "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", "r12", "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 mips64-linux, sizeof(long long) == 8. */ #define MIPS64_LONG2REG_CAST(x) ((long long)(long)x) #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[1]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_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)) (long)_res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[2]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(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)) (long)_res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[3]; \ volatile unsigned long long _res; \ _argvec[0] = _orig.nraddr; \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(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)) (long)_res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[4]; \ volatile unsigned long long _res; \ _argvec[0] = _orig.nraddr; \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(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)) (long)_res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[5]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(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)) (long)_res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[6]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(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)) (long)_res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[7]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[8]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[9]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[10]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[11]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[12]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ _argvec[11] = MIPS64_LONG2REG_CAST(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)) (long)_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 long _argvec[13]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ _argvec[12] = MIPS64_LONG2REG_CAST(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)) (long)_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 NUMERIC VALUES OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end of the most relevant group. */ 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, /* Some requests used for Valgrind internal, such as self-test or self-hosting. */ /* Initialise IR injection */ VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901, /* Used by Inner Valgrind to inform Outer Valgrind where to find the list of inner guest threads */ VG_USERREQ__INNER_THREADS = 0x1902 } 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) #define VALGRIND_INNER_THREADS(_qzz_addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__INNER_THREADS, \ _qzz_addr, 0, 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(const char *format, ...) { #if defined(NVALGRIND) (void)format; 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(const char *format, ...) { #if defined(NVALGRIND) (void)format; 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 arbitrary 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) /* Create a memory pool with some flags specifying extended behaviour. When flags is zero, the behaviour is identical to VALGRIND_CREATE_MEMPOOL. The flag VALGRIND_MEMPOOL_METAPOOL specifies that the pieces of memory associated with the pool using VALGRIND_MEMPOOL_ALLOC will be used by the application as superblocks to dole out MALLOC_LIKE blocks using VALGRIND_MALLOCLIKE_BLOCK. In other words, a meta pool is a "2 levels" pool : first level is the blocks described by VALGRIND_MEMPOOL_ALLOC. The second level blocks are described using VALGRIND_MALLOCLIKE_BLOCK. Note that the association between the pool and the second level blocks is implicit : second level blocks will be located inside first level blocks. It is necessary to use the VALGRIND_MEMPOOL_METAPOOL flag for such 2 levels pools, as otherwise valgrind will detect overlapping memory blocks, and will abort execution (e.g. during leak search). Such a meta pool can also be marked as an 'auto free' pool using the flag VALGRIND_MEMPOOL_AUTO_FREE, which must be OR-ed together with the VALGRIND_MEMPOOL_METAPOOL. For an 'auto free' pool, VALGRIND_MEMPOOL_FREE will automatically free the second level blocks that are contained inside the first level block freed with VALGRIND_MEMPOOL_FREE. In other words, calling VALGRIND_MEMPOOL_FREE will cause implicit calls to VALGRIND_FREELIKE_BLOCK for all the second level blocks included in the first level block. Note: it is an error to use the VALGRIND_MEMPOOL_AUTO_FREE flag without the VALGRIND_MEMPOOL_METAPOOL flag. */ #define VALGRIND_MEMPOOL_AUTO_FREE 1 #define VALGRIND_MEMPOOL_METAPOOL 2 #define VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, flags, 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 #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #endif /* __VALGRIND_H */ pmemkv-1.1/tests/000077500000000000000000000000001361504041500140135ustar00rootroot00000000000000pmemkv-1.1/tests/CMakeLists.txt000066400000000000000000000237031361504041500165600ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. include(ctest_helpers.cmake) add_cppstyle(tests ${CMAKE_CURRENT_SOURCE_DIR}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/engines/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/engines-experimental/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/config/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/compatibility/*.cc) add_check_whitespace(tests ${CMAKE_CURRENT_SOURCE_DIR}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/engines/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/engines-experimental/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/config/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/compatibility/*.cc) if (COVERAGE AND VALGRIND_FOUND) message(STATUS "This is the Coverage build, skipping Valgrind tests") endif() function(ignore_unused_but_set_variable_cpp target) check_cxx_compiler_flag(-Wno-unused-but-set-variable wno_unused_but_set_variable_flag_cpp) if (wno_unused_but_set_variable_flag_cpp) target_compile_options(${target} PUBLIC -Wno-unused-but-set-variable) endif() endfunction() function(ignore_unused_but_set_variable_c target) check_c_compiler_flag(-Wno-unused-but-set-variable wno_unused_but_set_variable_flag_c) if (wno_unused_but_set_variable_flag_c) target_compile_options(${target} PUBLIC -Wno-unused-but-set-variable) endif() endfunction() function(build_example_pmemkv_basic_cpp) add_executable(ex_pmemkv_basic_cpp ../examples/pmemkv_basic_cpp/pmemkv_basic.cpp) target_include_directories(ex_pmemkv_basic_cpp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) target_link_libraries(ex_pmemkv_basic_cpp pmemkv) ignore_unused_but_set_variable_cpp(ex_pmemkv_basic_cpp) endfunction() function(build_example_pmemkv_basic_c) add_executable(ex_pmemkv_basic_c ../examples/pmemkv_basic_c/pmemkv_basic.c) target_include_directories(ex_pmemkv_basic_c PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) target_link_libraries(ex_pmemkv_basic_c pmemkv) ignore_unused_but_set_variable_c(ex_pmemkv_basic_c) endfunction() function(build_example_pmemkv_pmemobj_cpp) if(NOT("${LIBPMEMOBJ++_LIBRARIES}" STREQUAL "")) add_executable(ex_pmemkv_pmemobj_cpp ../examples/pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp) target_include_directories(ex_pmemkv_pmemobj_cpp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) target_link_libraries(ex_pmemkv_pmemobj_cpp pmemkv ${LIBPMEMOBJ++_LIBRARIES}) ignore_unused_but_set_variable_cpp(ex_pmemkv_pmemobj_cpp) endif() endfunction() function(build_wrong_engine_name_test) add_executable(wrong_engine_name_test wrong_engine_name_test.cc) target_include_directories(wrong_engine_name_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) target_link_libraries(wrong_engine_name_test pmemkv) if(ENGINE_VSMAP) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_VSMAP) endif() if(ENGINE_VCMAP) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_VCMAP) endif() if(ENGINE_CMAP) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_CMAP) endif() if(ENGINE_CACHING) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_CACHING) endif() if(ENGINE_STREE) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_STREE) endif() if(ENGINE_TREE3) target_compile_definitions(wrong_engine_name_test PRIVATE -DENGINE_TREE3) endif() endfunction() set(TEST_FILES pmemkv_test.cc pmemkv_c_api_test.cc mock_tx_alloc.cc engines/blackhole_test.cc config/config_c.cc config/config_cpp.cc ) if(BUILD_JSON_CONFIG) list(APPEND TEST_FILES config/json_to_config.cc) endif() # add each engine's tests source files separately if(ENGINE_VSMAP) list(APPEND TEST_FILES engines/vsmap_test.cc) endif() if(ENGINE_VCMAP) list(APPEND TEST_FILES engines/vcmap_test.cc) endif() if(ENGINE_CMAP) list(APPEND TEST_FILES engines/cmap_test.cc) list(APPEND TEST_FILES engines/cmap_pmemobj_test.cc) endif() if(ENGINE_CACHING) if(ENGINE_TREE3 AND BUILD_JSON_CONFIG) list(APPEND TEST_FILES engines-experimental/caching_test.cc) elseif(NOT BUILD_JSON_CONFIG) message(WARNING "Caching tests require the 'libpmemkv_json_config' library, which is not built, " "hence they are disabled. If you want to run them use -DBUILD_JSON_CONFIG=ON option.") else() message(WARNING "Caching tests are set to work with TREE3 engine, which is disabled, hence " "they are also disabled. If you want to run them use -DENGINE_TREE3=ON option.") endif() endif() if(ENGINE_STREE) list(APPEND TEST_FILES engines-experimental/stree_test.cc) list(APPEND TEST_FILES engines-experimental/stree_pmemobj_test.cc) endif() if(ENGINE_TREE3) list(APPEND TEST_FILES engines-experimental/tree3_test.cc) list(APPEND TEST_FILES engines-experimental/tree3_pmemobj_test.cc) endif() # CMake option 'CMAKE_PREFIX_PATH' will be prioritized # over system paths in find_library and find_path calls find_library(GTEST NAMES gtest) if(GTEST) message(STATUS "Gtest found in ${GTEST}") find_path(GTEST_INCLUDEDIR "gtest/gtest.h") if (GTEST_INCLUDEDIR) message(STATUS "Gtest headers found in ${GTEST_INCLUDEDIR}") include_directories(${GTEST_INCLUDEDIR}) endif() add_library(libgtest IMPORTED STATIC GLOBAL) set_target_properties(libgtest PROPERTIES "IMPORTED_LOCATION" "${GTEST}" "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}") else() message(FATAL_ERROR "Gtest is not installed or couldn't be found. Try using cmake option " "-DCMAKE_PREFIX_PATH=. " "Use e.g. like 'cmake .. -DCMAKE_PREFIX_PATH=/install/path/gtest/'") endif() include(Dart) include(GoogleTest) add_executable(pmemkv_test ${TEST_FILES}) gtest_discover_tests(pmemkv_test EXTRA_ARGS --test_dir ${TEST_DIR} NO_PRETTY_VALUES) if (VALGRIND_FOUND AND NOT COVERAGE) add_executable(memcheck valgrind_wrapper.cc) add_dependencies(memcheck pmemkv_test) gtest_discover_tests(memcheck EXTRA_ARGS --test_dir ${TEST_DIR} NO_PRETTY_VALUES TEST_SUFFIX _MEMCHECK) add_executable(drd valgrind_wrapper.cc) add_dependencies(drd pmemkv_test) gtest_discover_tests(drd EXTRA_ARGS --test_dir ${TEST_DIR} NO_PRETTY_VALUES TEST_SUFFIX _DRD) add_executable(helgrind valgrind_wrapper.cc) add_dependencies(helgrind pmemkv_test) gtest_discover_tests(helgrind EXTRA_ARGS --test_dir ${TEST_DIR} NO_PRETTY_VALUES TEST_SUFFIX _HELGRIND) endif() if (VALGRIND_PMEMCHECK_FOUND AND NOT COVERAGE) add_executable(pmemcheck valgrind_wrapper.cc) add_dependencies(pmemcheck pmemkv_test) gtest_discover_tests(pmemcheck EXTRA_ARGS --test_dir ${TEST_DIR} NO_PRETTY_VALUES TEST_SUFFIX _PMEMCHECK) endif() target_link_libraries(pmemkv_test pmemkv libgtest ${CMAKE_DL_LIBS}) # XXX It should be removed when all supported distributions update gtest # to at least v1.9.0.20190831-1 # and the 'INSTANTIATE_TEST_CASE_P' macro can be replaced # with the 'INSTANTIATE_TEST_SUITE_P' macro. # See https://github.com/pmem/pmemkv/issues/527 for more details. target_compile_options(pmemkv_test PUBLIC "-Wno-deprecated-declarations") if(BUILD_JSON_CONFIG) target_link_libraries(pmemkv_test pmemkv_json_config) endif() if(ENGINE_CMAP) target_link_libraries(pmemkv_test ${LIBPMEMOBJ++_LIBRARIES}) endif() if(ENGINE_CACHING) # caching tests require lib_acl and memcached included, so we need to link # them to test binary itself target_link_libraries(pmemkv_test memcached) target_link_libraries(pmemkv_test acl_cpp protocol acl) endif() if(ENGINE_STREE) target_link_libraries(pmemkv_test ${LIBPMEMOBJ++_LIBRARIES}) endif() if(ENGINE_TREE3) target_link_libraries(pmemkv_test ${LIBPMEMOBJ++_LIBRARIES}) endif() # save lists of source files and all tests in files to check them in the first test set(FILE_TEST_FILES ${CMAKE_CURRENT_BINARY_DIR}/test_files.txt) set(FILE_ALL_TESTS ${CMAKE_CURRENT_BINARY_DIR}/list_all_tests.txt) file(WRITE ${FILE_TEST_FILES} "${TEST_FILES}") file(WRITE ${FILE_ALL_TESTS} "${list_all_tests}") build_wrong_engine_name_test() test("run-binary" wrong_engine_name_test wrong_engine_name_test none) if(BUILD_EXAMPLES AND ENGINE_CMAP) build_example_pmemkv_basic_c() build_example_pmemkv_basic_cpp() build_example_pmemkv_pmemobj_cpp() test("run-binary" ex_pmemkv_basic_c ex_pmemkv_basic_c none) test("run-binary" ex_pmemkv_basic_cpp ex_pmemkv_basic_cpp none) test("run-binary" ex_pmemkv_pmemobj_cpp ex_pmemkv_pmemobj_cpp none) elseif(BUILD_EXAMPLES AND NOT ENGINE_CMAP) message(WARNING "Examples use cmap engine, which is disabled, hence their execution " "is also disabled. If you want to run them use -DENGINE_CMAP=ON option.") endif() # test pmreorder framework if(PMREORDER_SUPPORTED) test("run-pmreorder-test" wrong_engine_name_test-pmreorder wrong_engine_name_test pmreorder) endif() pmemkv-1.1/tests/basic_tests.h000066400000000000000000000067261361504041500165020ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef BASIC_TESTS_H #define BASIC_TESTS_H #include "test_path.h" #include "test_suite.h" #include static Basic basic_tests[] = { { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 0, .engine = "blackhole", .key_length = 0, .value_length = 0, .test_value_length = 0, .name = "BlackholeEmptyData", .tracers = "MP", .use_file = false, }, #ifdef ENGINE_CMAP { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 1, .engine = "cmap", .key_length = 100, .value_length = 100, .test_value_length = 20, .name = "CMapTest100bKey100bValue", .tracers = "MPHD", .use_file = true, }, #endif // ENGINE_CMAP #ifdef ENGINE_VSMAP { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 1, .engine = "vsmap", .key_length = 100, .value_length = 100, .test_value_length = 20, .name = "VSMapTest100bKey100bValue", .tracers = "MP", .use_file = false, }, #endif // ENGINE_VSMAP #ifdef ENGINE_VCMAP { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 1, .engine = "vcmap", .key_length = 100, .value_length = 100, .test_value_length = 20, .name = "VCMapTest100bKey100bValue", .tracers = "MPHD", .use_file = false, }, #endif // ENGINE_VCMAP #ifdef ENGINE_TREE3 { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 1, .engine = "tree3", .key_length = 100, .value_length = 100, .test_value_length = 20, .name = "Tree3Test100bKey100bValue", .tracers = "", .use_file = true, }, #endif // ENGINE_TREE3 #ifdef ENGINE_STREE { .path = &test_path, .size = (uint64_t)(1024 * 1024 * 1024), .force_create = 1, .engine = "stree", .key_length = 20, .value_length = 200, .test_value_length = 20, .name = "StreeTest20bKey200bValue", .tracers = "", .use_file = true, }, #endif // ENGINE_STREE }; #endif // BASIC_TESTS_H_ pmemkv-1.1/tests/compatibility/000077500000000000000000000000001361504041500166645ustar00rootroot00000000000000pmemkv-1.1/tests/compatibility/CMakeLists.txt000066400000000000000000000040301361504041500214210ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. project(pmemkv_compatibility_test) cmake_minimum_required(VERSION 3.10) include(../ctest_helpers.cmake) find_package(PkgConfig REQUIRED) enable_testing() pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) function(build_binary name) add_executable(${name} ${ARGN}) target_include_directories(${name} PRIVATE ${LIBPMEMKV_INCLUDE_DIRS}) target_link_directories(${name} PRIVATE ${LIBPMEMKV_LIBRARY_DIRS}) target_link_libraries(${name} pmemkv) endfunction() build_binary(cmap_compatibility cmap.cc) pmemkv-1.1/tests/compatibility/README.md000066400000000000000000000002351361504041500201430ustar00rootroot00000000000000This folder contains tests which can be used to test compatibility between different pmemkv versions. For usage, see ../../utils/docker/run-compatibility.sh pmemkv-1.1/tests/compatibility/cmap.cc000066400000000000000000000072461361504041500201240ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include #include const uint64_t SIZE = 1024UL * 1024UL * 1024UL; const uint64_t NUM_ELEMENTS = 1024UL * 1024UL; pmem::kv::db *db_create(std::string path) { pmem::kv::config cfg; pmem::kv::status s = cfg.put_string("path", path); assert(s == pmem::kv::status::OK); s = cfg.put_uint64("size", SIZE); assert(s == pmem::kv::status::OK); s = cfg.put_uint64("force_create", 1); assert(s == pmem::kv::status::OK); pmem::kv::db *kv = new pmem::kv::db; s = kv->open("cmap", std::move(cfg)); assert(s == pmem::kv::status::OK); return kv; } pmem::kv::db *db_open(std::string path) { pmem::kv::config cfg; pmem::kv::status s = cfg.put_string("path", path); assert(s == pmem::kv::status::OK); pmem::kv::db *kv = new pmem::kv::db; s = kv->open("cmap", std::move(cfg)); assert(s == pmem::kv::status::OK); return kv; } void populate_db(pmem::kv::db &db, size_t num_elements) { for (size_t i = 0; i < num_elements; i++) { auto s = db.put(std::to_string(i), std::to_string(i)); assert(s == pmem::kv::status::OK); } } void verify_db(pmem::kv::db &db, size_t num_elements) { std::size_t count; auto s = db.count_all(count); assert(s == pmem::kv::status::OK); assert(count == num_elements); for (size_t i = 0; i < num_elements; i++) { auto s = db.get(std::to_string(i), [&](pmem::kv::string_view value) { assert(std::to_string(i).compare(0, std::string::npos, value.data()) == 0); }); assert(s == pmem::kv::status::OK); } } int main(int argc, char *argv[]) { if (argc < 3) { std::cerr << "Usage: " << argv[0] << " file [create|create_ungraceful|open]\n"; exit(1); } pmem::kv::db *db; if (std::string(argv[2]) == "create") { db = db_create(argv[1]); populate_db(*db, NUM_ELEMENTS); delete db; } else if (std::string(argv[2]) == "create_ungraceful") { db = db_create(argv[1]); populate_db(*db, NUM_ELEMENTS); /* Do not close db */ } else if (std::string(argv[2]) == "open") { db = db_open(argv[1]); verify_db(*db, NUM_ELEMENTS); delete db; } else { std::cerr << "Wrong mode\n"; exit(1); } return 0; } pmemkv-1.1/tests/compatibility/cmap.sh000077500000000000000000000036731361504041500201540ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # cmap.sh -- runs cmap compatibility test # set -e binary1=$1 binary2=$2 testfile=$3 rm -f $testfile $binary1 $testfile create $binary2 $testfile open rm -f $testfile $binary1 $testfile create_ungraceful $binary2 $testfile open rm -f $testfile $binary2 $testfile create $binary1 $testfile open rm -f $testfile $binary2 $testfile create_ungraceful $binary1 $testfile open rm -f $testfile pmemkv-1.1/tests/config/000077500000000000000000000000001361504041500152605ustar00rootroot00000000000000pmemkv-1.1/tests/config/config_c.cc000066400000000000000000000175261361504041500173510ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" class ConfigCTest : public testing::Test { public: pmemkv_config *config; ConfigCTest() { config = pmemkv_config_new(); } ~ConfigCTest() { if (config) pmemkv_config_delete(config); } }; struct custom_type { int a; char b; }; static void deleter(custom_type *ct_ptr) { ct_ptr->a = -1; ct_ptr->b = '0'; } TEST_F(ConfigCTest, SimpleTest_TRACERS_M) { auto ret = pmemkv_config_put_string(config, "string", "abc"); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_int64(config, "int", 123); ASSERT_EQ(ret, PMEMKV_STATUS_OK); custom_type *ptr = new custom_type; ptr->a = 10; ptr->b = 'a'; ret = pmemkv_config_put_object(config, "object_ptr", ptr, nullptr); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_data(config, "object", ptr, sizeof(*ptr)); ASSERT_EQ(ret, PMEMKV_STATUS_OK); custom_type *ptr_deleter = new custom_type; ptr_deleter->a = 11; ptr_deleter->b = 'b'; ret = pmemkv_config_put_object(config, "object_ptr_with_deleter", ptr_deleter, (void (*)(void *)) & deleter); ASSERT_EQ(ret, PMEMKV_STATUS_OK); const char *value_string; ret = pmemkv_config_get_string(config, "string", &value_string); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_TRUE(std::string(value_string) == "abc"); int64_t value_int; ret = pmemkv_config_get_int64(config, "int", &value_int); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_int, 123); custom_type *value_custom_ptr; ret = pmemkv_config_get_object(config, "object_ptr", (void **)&value_custom_ptr); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_custom_ptr->a, 10); ASSERT_EQ(value_custom_ptr->b, 'a'); custom_type *value_custom_ptr_deleter; ret = pmemkv_config_get_object(config, "object_ptr_with_deleter", (void **)&value_custom_ptr_deleter); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_custom_ptr_deleter->a, 11); ASSERT_EQ(value_custom_ptr_deleter->b, 'b'); custom_type *value_custom; size_t value_custom_size; ret = pmemkv_config_get_data(config, "object", (const void **)&value_custom, &value_custom_size); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_custom_size, sizeof(value_custom)); ASSERT_EQ(value_custom->a, 10); ASSERT_EQ(value_custom->b, 'a'); int64_t none; ASSERT_EQ(pmemkv_config_get_int64(config, "non-existent", &none), PMEMKV_STATUS_NOT_FOUND); delete ptr; pmemkv_config_delete(config); config = nullptr; ASSERT_EQ(value_custom_ptr_deleter->a, -1); ASSERT_EQ(value_custom_ptr_deleter->b, '0'); delete ptr_deleter; } TEST_F(ConfigCTest, IntegralConversionTest_TRACERS_M) { auto ret = pmemkv_config_put_int64(config, "int", 123); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_uint64(config, "uint", 123); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_int64(config, "negative-int", -123); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_uint64(config, "uint-max", std::numeric_limits::max()); ASSERT_EQ(ret, PMEMKV_STATUS_OK); int64_t int_s; ret = pmemkv_config_get_int64(config, "int", &int_s); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(int_s, 123); size_t int_us; ret = pmemkv_config_get_uint64(config, "int", &int_us); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(int_us, 123U); int64_t uint_s; ret = pmemkv_config_get_int64(config, "uint", &uint_s); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(uint_s, 123); size_t uint_us; ret = pmemkv_config_get_uint64(config, "uint", &uint_us); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(uint_us, 123U); int64_t neg_int_s; ret = pmemkv_config_get_int64(config, "negative-int", &neg_int_s); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(neg_int_s, -123); size_t neg_int_us; ret = pmemkv_config_get_uint64(config, "negative-int", &neg_int_us); ASSERT_EQ(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); int64_t uint_max_s; ret = pmemkv_config_get_int64(config, "uint-max", &uint_max_s); ASSERT_EQ(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); size_t uint_max_us; ret = pmemkv_config_get_uint64(config, "uint-max", &uint_max_us); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(uint_max_us, std::numeric_limits::max()); } TEST_F(ConfigCTest, NotFoundTest_TRACERS_M) { /* all gets should return NotFound when looking for non-existing key */ const char *my_string; int ret = pmemkv_config_get_string(config, "non-existent-string", &my_string); ASSERT_EQ(ret, PMEMKV_STATUS_NOT_FOUND); int64_t my_int; ret = pmemkv_config_get_int64(config, "non-existent-int", &my_int); ASSERT_EQ(ret, PMEMKV_STATUS_NOT_FOUND); size_t my_uint; ret = pmemkv_config_get_uint64(config, "non-existent-uint", &my_uint); ASSERT_EQ(ret, PMEMKV_STATUS_NOT_FOUND); custom_type *my_object; ret = pmemkv_config_get_object(config, "non-existent-object", (void **)&my_object); ASSERT_EQ(ret, PMEMKV_STATUS_NOT_FOUND); size_t my_object_size = 0; ret = pmemkv_config_get_data(config, "non-existent-data", (const void **)&my_object, &my_object_size); ASSERT_EQ(ret, PMEMKV_STATUS_NOT_FOUND); ASSERT_EQ(my_object_size, 0U); } /* Test if null can be passed as config to pmemkv_config_* functions */ TEST_F(ConfigCTest, NullConfigTest) { auto ret = pmemkv_config_put_string(nullptr, "string", "abc"); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); ret = pmemkv_config_put_int64(nullptr, "int", 123); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); custom_type *ptr = new custom_type; ptr->a = 10; ptr->b = 'a'; ret = pmemkv_config_put_object(nullptr, "object_ptr", ptr, nullptr); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); ret = pmemkv_config_put_data(nullptr, "object", ptr, sizeof(*ptr)); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); const char *value_string; ret = pmemkv_config_get_string(nullptr, "string", &value_string); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); int64_t value_int; ret = pmemkv_config_get_int64(nullptr, "int", &value_int); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); custom_type *value_custom_ptr; ret = pmemkv_config_get_object(nullptr, "object_ptr", (void **)&value_custom_ptr); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); custom_type *value_custom; size_t value_custom_size; ret = pmemkv_config_get_data(nullptr, "object", (const void **)&value_custom, &value_custom_size); ASSERT_EQ(ret, PMEMKV_STATUS_INVALID_ARGUMENT); delete ptr; } pmemkv-1.1/tests/config/config_cpp.cc000066400000000000000000000166151361504041500177070ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" using namespace pmem::kv; class ConfigCppTest : public testing::Test { public: config *cfg; ConfigCppTest() { cfg = new config(); } ~ConfigCppTest() { if (cfg) delete cfg; } }; struct custom_type { int a; char b; }; static void deleter(custom_type *ct_ptr) { ct_ptr->a = -1; ct_ptr->b = '0'; } TEST_F(ConfigCppTest, SimpleTest_TRACERS_M) { status s = cfg->put_string("string", "abc"); ASSERT_EQ(s, status::OK); s = cfg->put_int64("int", 123); ASSERT_EQ(s, status::OK); custom_type *ptr = new custom_type; ptr->a = 10; ptr->b = 'a'; s = cfg->put_object("object_ptr", ptr, nullptr); ASSERT_EQ(s, status::OK); s = cfg->put_data("object", ptr); ASSERT_EQ(s, status::OK); int array[3] = {1, 15, 77}; s = cfg->put_data("array", array, 3); ASSERT_EQ(s, status::OK); custom_type *ptr_deleter = new custom_type; ptr_deleter->a = 11; ptr_deleter->b = 'b'; s = cfg->put_object("object_ptr_with_deleter", ptr_deleter, (void (*)(void *)) & deleter); ASSERT_EQ(s, status::OK); std::string value_string; s = cfg->get_string("string", value_string); ASSERT_EQ(s, status::OK); ASSERT_TRUE(value_string == "abc"); int64_t value_int; s = cfg->get_int64("int", value_int); ASSERT_EQ(s, status::OK); ASSERT_EQ(value_int, 123); custom_type *value_custom_ptr; s = cfg->get_object("object_ptr", value_custom_ptr); ASSERT_EQ(s, status::OK); ASSERT_EQ(value_custom_ptr->a, 10); ASSERT_EQ(value_custom_ptr->b, 'a'); custom_type *value_custom_ptr_deleter; s = cfg->get_object("object_ptr_with_deleter", value_custom_ptr_deleter); ASSERT_EQ(s, status::OK); ASSERT_EQ(value_custom_ptr_deleter->a, 11); ASSERT_EQ(value_custom_ptr_deleter->b, 'b'); custom_type *value_custom; size_t value_custom_count; s = cfg->get_data("object", value_custom, value_custom_count); ASSERT_EQ(s, status::OK); ASSERT_EQ(value_custom_count, 1U); ASSERT_EQ(value_custom->a, 10); ASSERT_EQ(value_custom->b, 'a'); int *value_array; size_t value_array_count; s = cfg->get_data("array", value_array, value_array_count); ASSERT_EQ(s, status::OK); ASSERT_EQ(value_array_count, 3U); ASSERT_EQ(value_array[0], 1); ASSERT_EQ(value_array[1], 15); ASSERT_EQ(value_array[2], 77); int64_t none; ASSERT_EQ(cfg->get_int64("non-existent", none), status::NOT_FOUND); delete ptr; delete cfg; cfg = nullptr; ASSERT_EQ(value_custom_ptr_deleter->a, -1); ASSERT_EQ(value_custom_ptr_deleter->b, '0'); delete ptr_deleter; } TEST_F(ConfigCppTest, IntegralConversionTest_TRACERS_M) { status s = cfg->put_int64("int", 123); ASSERT_EQ(s, status::OK); s = cfg->put_uint64("uint", 123); ASSERT_EQ(s, status::OK); s = cfg->put_int64("negative-int", -123); ASSERT_EQ(s, status::OK); s = cfg->put_uint64("uint-max", std::numeric_limits::max()); ASSERT_EQ(s, status::OK); int64_t int_s; s = cfg->get_int64("int", int_s); ASSERT_EQ(s, status::OK); ASSERT_EQ(int_s, 123); size_t int_us; s = cfg->get_uint64("int", int_us); ASSERT_EQ(s, status::OK); ASSERT_EQ(int_us, 123U); int64_t uint_s; s = cfg->get_int64("uint", uint_s); ASSERT_EQ(s, status::OK); ASSERT_EQ(uint_s, 123); size_t uint_us; s = cfg->get_uint64("uint", uint_us); ASSERT_EQ(s, status::OK); ASSERT_EQ(uint_us, 123U); int64_t neg_int_s; s = cfg->get_int64("negative-int", neg_int_s); ASSERT_EQ(s, status::OK); ASSERT_EQ(neg_int_s, -123); size_t neg_int_us; s = cfg->get_uint64("negative-int", neg_int_us); ASSERT_EQ(s, status::CONFIG_TYPE_ERROR); int64_t uint_max_s; s = cfg->get_int64("uint-max", uint_max_s); ASSERT_EQ(s, status::CONFIG_TYPE_ERROR); size_t uint_max_us; s = cfg->get_uint64("uint-max", uint_max_us); ASSERT_EQ(s, status::OK); ASSERT_EQ(uint_max_us, std::numeric_limits::max()); } TEST_F(ConfigCppTest, ConstructorsTest_TRACERS_M) { /* assign released C++ config to C config; * it's null because config is lazy initialized */ pmemkv_config *c_cfg = cfg->release(); ASSERT_EQ(c_cfg, nullptr); /* put value to C++ config */ auto s = cfg->put_int64("int", 65535); ASSERT_EQ(s, status::OK); /* use move constructor and test if data is still accessible */ config *move_config = new config(std::move(*cfg)); int64_t int_s; s = move_config->get_int64("int", int_s); ASSERT_EQ(s, status::OK); ASSERT_EQ(int_s, 65535); /* release new C++ config and test if data is accessible in C config */ c_cfg = move_config->release(); auto ret = pmemkv_config_get_int64(c_cfg, "int", &int_s); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(int_s, 65535); /* check if moved config is empty */ s = move_config->get_int64("int", int_s); ASSERT_EQ(s, status::NOT_FOUND); /* cleanup */ pmemkv_config_delete(c_cfg); delete move_config; } TEST_F(ConfigCppTest, NotFoundTest_TRACERS_M) { /* config is nullptr; all gets should return NotFound */ std::string my_string; int64_t my_int; uint64_t my_uint; custom_type *my_object; size_t my_object_count = 0; ASSERT_EQ(cfg->get_string("string", my_string), status::NOT_FOUND); ASSERT_EQ(cfg->get_int64("int", my_int), status::NOT_FOUND); ASSERT_EQ(cfg->get_uint64("uint", my_uint), status::NOT_FOUND); ASSERT_EQ(cfg->get_object("object", my_object), status::NOT_FOUND); ASSERT_EQ(cfg->get_data("data", my_object, my_object_count), status::NOT_FOUND); ASSERT_EQ(my_object_count, 0U); /* initialize config with any put */ cfg->put_int64("init", 0); /* all gets should return NotFound when looking for non-existing key */ ASSERT_EQ(cfg->get_string("non-existent-string", my_string), status::NOT_FOUND); ASSERT_EQ(cfg->get_int64("non-existent-int", my_int), status::NOT_FOUND); ASSERT_EQ(cfg->get_uint64("non-existent-uint", my_uint), status::NOT_FOUND); ASSERT_EQ(cfg->get_object("non-existent-object_ptr", my_object), status::NOT_FOUND); ASSERT_EQ(cfg->get_data("non-existent-data", my_object, my_object_count), status::NOT_FOUND); ASSERT_EQ(my_object_count, 0U); } pmemkv-1.1/tests/config/json_to_config.cc000066400000000000000000000064071361504041500205760ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "../../src/libpmemkv_json_config.h" #include "gtest/gtest.h" class JsonToConfigTest : public testing::Test { public: pmemkv_config *config; JsonToConfigTest() { config = pmemkv_config_new(); } ~JsonToConfigTest() { pmemkv_config_delete(config); } }; TEST_F(JsonToConfigTest, SimpleTest_TRACERS_M) { auto ret = pmemkv_config_from_json( config, "{\"string\": \"abc\", \"int\": 123, \"bool\": true}"); // XXX: extend by adding "false", subconfig, negative value ASSERT_EQ(ret, PMEMKV_STATUS_OK); const char *value_string; ret = pmemkv_config_get_string(config, "string", &value_string); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_TRUE(std::string(value_string) == "abc"); int64_t value_int; ret = pmemkv_config_get_int64(config, "int", &value_int); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_int, 123); int64_t value_bool; ret = pmemkv_config_get_int64(config, "bool", &value_bool); ASSERT_EQ(ret, PMEMKV_STATUS_OK); ASSERT_EQ(value_bool, 1); ret = pmemkv_config_get_int64(config, "string", &value_int); ASSERT_EQ(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); } TEST_F(JsonToConfigTest, DoubleTest_TRACERS_M) { auto ret = pmemkv_config_from_json(config, "{\"double\": 12.34}"); ASSERT_EQ(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); ASSERT_EQ( std::string(pmemkv_config_from_json_errormsg()), "[pmemkv_config_from_json] Unsupported data type in JSON string: Number"); } TEST_F(JsonToConfigTest, MalformedInput_TRACERS_M) { auto ret = pmemkv_config_from_json(config, "{\"int\": 12"); ASSERT_EQ(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); ASSERT_EQ(std::string(pmemkv_config_from_json_errormsg()), "[pmemkv_config_from_json] Config parsing failed"); } pmemkv-1.1/tests/ctest_helpers.cmake000066400000000000000000000062231361504041500176640ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. set(DEFAULT_TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) set(TEST_DIR ${DEFAULT_TEST_DIR} CACHE STRING "working directory for tests") set(GLOBAL_TEST_ARGS -DPARENT_DIR=${TEST_DIR}/) option(TRACE_TESTS "enable expanded tests' tracing" OFF) if(TRACE_TESTS) set(GLOBAL_TEST_ARGS ${GLOBAL_TEST_ARGS} --trace-expand) endif() set(vg_tracers memcheck helgrind drd pmemcheck pmreorder) set(pmemcheck_tracers pmemcheck pmreorder) file(MAKE_DIRECTORY ${DEFAULT_TEST_DIR}) # # test -- add a test 'test_name' # # Arguments: # cmake_file - cmake file to run the test # test_name - name of a test to be printed out by ctest (must be unique) # test_filter - name of a test in the gtest binary (used as a gtest filter) # tracer - Valgrind tool (memcheck/helgrind/drd/pmemcheck) # or pmreorder used to trace the test # function(test cmake_file test_name test_filter tracer) if (${tracer} IN_LIST vg_tracers) if (NOT VALGRIND_FOUND) return() endif() if (COVERAGE) return() endif() endif() if (${tracer} IN_LIST pmemcheck_tracers) if (NOT VALGRIND_PMEMCHECK_FOUND) return() endif() endif() add_test(NAME ${test_name} COMMAND ${CMAKE_COMMAND} ${GLOBAL_TEST_ARGS} -DTEST_NAME=${test_filter} -DSRC_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBIN_DIR=${TEST_DIR}/${test_filter}-${tracer} -DCONFIG=$ -DTRACER=${tracer} -DFILE_TEST_FILES=${FILE_TEST_FILES} -DFILE_ALL_TESTS=${FILE_ALL_TESTS} ${ARGN} -P ${CMAKE_CURRENT_SOURCE_DIR}/${cmake_file}.cmake) set_tests_properties(${test_name} PROPERTIES ENVIRONMENT "LC_ALL=C;PATH=$ENV{PATH};PMEM_IS_PMEM_FORCE=1" TIMEOUT 300) endfunction() pmemkv-1.1/tests/drd.supp000066400000000000000000000000001361504041500154630ustar00rootroot00000000000000pmemkv-1.1/tests/engines-experimental/000077500000000000000000000000001361504041500201365ustar00rootroot00000000000000pmemkv-1.1/tests/engines-experimental/caching_test.cc000066400000000000000000001015011361504041500230760ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "../../src/libpmemkv_json_config.h" #include "lib_acl.hpp" #include "gtest/gtest.h" #include using namespace pmem::kv; // const std::string ENGINE = "stree"; const std::string ENGINE = "tree3"; const std::string PATH = "/dev/shm/pmemkv"; // const std::string ENGINE = "vcmap"; // const std::string ENGINE = "vsmap"; // const std::string PATH = "/dev/shm"; class CachingTest : public testing::Test { public: CachingTest() { std::remove(PATH.c_str()); } int start(const std::string &engine, const std::string &json) { pmemkv_config *cfg = pmemkv_config_new(); if (cfg == nullptr) throw std::runtime_error("Cannot create config"); // TODO: update with C++ config API (get rid of json?) auto ret = pmemkv_config_from_json(cfg, json.c_str()); if (ret != 0) throw std::runtime_error("Cannot parse json"); kv = new db; if (kv->open(engine, config(cfg)) != status::OK) throw std::runtime_error(errormsg()); pmemkv_config_delete(cfg); return kv != nullptr; } ~CachingTest() { if (kv) delete kv; } db *kv; }; TEST_F(CachingTest, PutKeyValue) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->exists("key1") == status::OK); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(CachingTest, PutUpdateValue) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->exists("key1") == status::OK); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->put("key1", "value11") == status::OK) << errormsg(); ASSERT_TRUE(kv->exists("key1") == status::OK); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value11"); } TEST_F(CachingTest, PutKeywithinTTL) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); sleep(1); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->exists("key1") == status::OK); } TEST_F(CachingTest, PutKeyExpiredTTL) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(2); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); } TEST_F(CachingTest, EmptyKeyTest) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("&*", " ") == status::OK) << errormsg(); std::string value1, value2, value3, value4; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); ASSERT_TRUE(status::OK == kv->exists("&*")); ASSERT_TRUE(kv->get("&*", &value4) == status::OK && value4 == " "); } TEST_F(CachingTest, EmptyValueTest) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); std::string value1, value2, value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(CachingTest, SimpleMemcached) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":11211,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Memcached\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); memcached_server_st *servers = NULL; memcached_st *memc; memcached_return rc; const char *key = "key1"; const char *value1 = "value1"; memc = memcached_create(NULL); // XXX: need to check if server doesn't exist already servers = memcached_server_list_append(servers, (char *)"127.0.0.1", 11211, &rc); rc = memcached_server_push(memc, servers); if (rc == MEMCACHED_SUCCESS) rc = memcached_set(memc, key, strlen(key), value1, strlen(value1), (time_t)0, (uint32_t)0); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); // getting the key from remote ASSERT_TRUE(kv->exists("key1") == status::OK); } TEST_F(CachingTest, SimpleRedis) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); int conn_timeout = 30, rw_timeout = 10; acl::string addr("127.0.0.1:6379"), passwd; acl::redis_client client(addr.c_str(), conn_timeout, rw_timeout); acl::redis cmd(&client); cmd.set("key1", "value1"); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); } TEST_F(CachingTest, UnknownLocalMemcachedKey) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":11211,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Memcached\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); memcached_server_st *servers = NULL; memcached_st *memc; memcached_return rc; const char *key = "key1"; char *return_value; uint32_t flags; size_t return_value_length; memc = memcached_create(NULL); servers = memcached_server_list_append(servers, (char *)"127.0.0.1", 11211, &rc); rc = memcached_server_push(memc, servers); if (rc == MEMCACHED_SUCCESS) { memcached_delete(memc, key, strlen(key), (time_t)0); return_value = memcached_get(memc, key, strlen(key), &return_value_length, &flags, &rc); } ASSERT_TRUE(return_value == NULL); // key is not present in memcached std::string val; ASSERT_TRUE(kv->get("key1", &val) == status::NOT_FOUND); } TEST_F(CachingTest, UnknownLocalRedisKey) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); int conn_timeout = 30, rw_timeout = 10; acl::string addr("127.0.0.1:6379"), passwd; acl::redis_client client(addr.c_str(), conn_timeout, rw_timeout); acl::redis cmd(&client); cmd.del("key1"); ASSERT_TRUE(cmd.exists("key1") == 0); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); } TEST_F(CachingTest, SimpleGetAllTest) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key4", "value4") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); if (ENGINE == "tree3") ASSERT_TRUE( result == ",|,|,|,|"); else if (ENGINE == "vcmap") ASSERT_TRUE( result == ",|,|,|,|"); else if (ENGINE == "vsmap") ASSERT_TRUE( result == ",|,|,|,|"); else ASSERT_TRUE( result == ",|,|,|,|"); } TEST_F(CachingTest, GetAllTTLValidExpired) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key4", "value4") == status::OK) << errormsg(); sleep(2); ASSERT_TRUE(kv->put("key5", "value5") == status::OK) << errormsg(); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); } TEST_F(CachingTest, GetAllEmptyCache) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ""); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); } TEST_F(CachingTest, GetAllZeroTTL) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key4", "value4") == status::OK) << errormsg(); sleep(1); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); if (ENGINE == "tree3") ASSERT_TRUE( result == ",|,|,|,|"); else if (ENGINE == "vcmap") ASSERT_TRUE( result == ",|,|,|,|"); else if (ENGINE == "vsmap") ASSERT_TRUE( result == ",|,|,|,|"); else ASSERT_TRUE( result == ",|,|,|,|"); } TEST_F(CachingTest, SimpleCount) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); sleep(2); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); } TEST_F(CachingTest, SimpleZeroTTLCount) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); sleep(1); ASSERT_TRUE(kv->put("key4", "value4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key5", "value5") == status::OK) << errormsg(); sleep(1); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); } TEST_F(CachingTest, SimpleRemovekey) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->exists("key1") == status::OK); ASSERT_TRUE(kv->remove("key1") == status::OK); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->remove("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); sleep(2); ASSERT_TRUE(kv->remove("key2") == status::OK); } TEST_F(CachingTest, SimpleExistsKey) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->exists("key1") == status::OK); sleep(1); // key1 not expired even after 1+1 sec sleep as Exists above updated on local // cache cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->exists("key1") == status::OK); sleep(2); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); } TEST_F(CachingTest, Redis_Integration) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->remove("key1") == status::OK); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->exists("key1") == status::OK); sleep(1); ASSERT_TRUE(kv->exists("key1") == status::OK); // key1 is not expired though the sleep is 1+1 sec // as Exists API above updated the timestamp sleep(2); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->exists("key2") == status::NOT_FOUND); ASSERT_TRUE(kv->exists("key3") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); // Remote redis connection int conn_timeout = 30, rw_timeout = 10; acl::string addr("127.0.0.1:6379"), passwd; acl::redis_client client(addr.c_str(), conn_timeout, rw_timeout); acl::redis cmd(&client); cmd.set("key1", "value1"); cmd.set("key2", "value2"); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->exists("key1") == status::OK); value = ""; ASSERT_TRUE(kv->get("key2", &value) == status::OK && value == "value2"); ASSERT_TRUE(kv->exists("key2") == status::OK); value = ""; ASSERT_TRUE(kv->get("key3", &value) == status::OK && value == "value3"); ASSERT_TRUE(kv->exists("key3") == status::OK); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); if (ENGINE == "tree3") ASSERT_TRUE(result == ",|,|,|"); else if (ENGINE == "vcmap") ASSERT_TRUE(result == ",|,|,|"); else if (ENGINE == "vsmap") ASSERT_TRUE(result == ",|,|,|"); else ASSERT_TRUE(result == ",|,|,|"); sleep(2); result = ""; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ""); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; ASSERT_TRUE(kv->get("key2", &value) == status::OK && value == "value2"); value = ""; ASSERT_TRUE(kv->get("key3", &value) == status::NOT_FOUND); #if 0 result = ""; kv->all( [](const char *k, size_t kb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,"); }, &result); if (ENGINE == "tree3") ASSERT_TRUE(result == ",,"); else if (ENGINE == "vcmap") ASSERT_TRUE(result == ",,"); else if (ENGINE == "vsmap") ASSERT_TRUE(result == ",,"); else ASSERT_TRUE(result == ",,"); sleep(2); result = ""; kv->all( [](const char *k, size_t kb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,"); }, &result); ASSERT_TRUE(result == ""); #endif cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); cmd.del("key1"); cmd.del("key2"); ASSERT_TRUE(cmd.exists("key1") == 0); ASSERT_TRUE(cmd.exists("key2") == 0); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("key2", &value) == status::NOT_FOUND); cmd.set("key1", "value1"); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->exists("key1") == status::OK); cmd.del("key1"); } TEST_F(CachingTest, Memcached_Integration) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":11211,\"attempts\":5,\"ttl\":1,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Memcached\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->remove("key1") == status::OK); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->exists("key1") == status::OK); sleep(1); ASSERT_TRUE(kv->exists("key1") == status::OK); // key1 is not expired though the sleep is 1+1 sec // as Exists API above updated the timestamp sleep(2); // TTL is 1 sec ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->exists("key2") == status::NOT_FOUND); ASSERT_TRUE(kv->exists("key3") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); // Remote memcached connection memcached_server_st *servers = NULL; memcached_st *memc; memcached_return rc; size_t return_value_length; uint32_t flags; char *return_value1, *return_value2; const int key_length = 4; const int value_length = 6; memc = memcached_create(NULL); servers = memcached_server_list_append(servers, (char *)"127.0.0.1", 11211, &rc); rc = memcached_server_push(memc, servers); if (rc == MEMCACHED_SUCCESS) { rc = memcached_set(memc, "key1", key_length, "value1", value_length, (time_t)0, (uint32_t)0); } value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); // getting the key from remote ASSERT_TRUE(kv->exists("key1") == status::OK); memcached_set(memc, "key2", key_length, "value2", value_length, (time_t)0, (uint32_t)0); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->exists("key1") == status::OK); value = ""; ASSERT_TRUE(kv->get("key2", &value) == status::OK && value == "value2"); ASSERT_TRUE(kv->exists("key2") == status::OK); value = ""; ASSERT_TRUE(kv->get("key3", &value) == status::OK && value == "value3"); ASSERT_TRUE(kv->exists("key3") == status::OK); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); if (ENGINE == "tree3") ASSERT_TRUE(result == ",|,|,|"); else if (ENGINE == "vcmap") ASSERT_TRUE(result == ",|,|,|"); else if (ENGINE == "vsmap") ASSERT_TRUE(result == ",|,|,|"); else ASSERT_TRUE(result == ",|,|,|"); sleep(2); result = ""; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ""); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; ASSERT_TRUE(kv->get("key2", &value) == status::OK && value == "value2"); value = ""; ASSERT_TRUE(kv->get("key3", &value) == status::NOT_FOUND); #if 0 result = ""; kv->all( [](const char *k, size_t kb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,"); }, &result); if (ENGINE == "tree3") ASSERT_TRUE(result == ",,"); else if (ENGINE == "vcmap") ASSERT_TRUE(result == ",,"); else if (ENGINE == "vsmap") ASSERT_TRUE(result == ",,"); else ASSERT_TRUE(result == ",,"); sleep(2); result = ""; kv->all( [](const char *k, size_t kb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,"); }, &result); ASSERT_TRUE(result == ""); #endif cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); memcached_delete(memc, "key1", key_length, (time_t)0); memcached_delete(memc, "key2", key_length, (time_t)0); return_value1 = memcached_get(memc, "key1", key_length, &return_value_length, &flags, &rc); ASSERT_TRUE(return_value1 == NULL); // key is not present in memcached return_value2 = memcached_get(memc, "key2", key_length, &return_value_length, &flags, &rc); ASSERT_TRUE(return_value2 == NULL); // key is not present in memcached value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("key2", &value) == status::NOT_FOUND); memcached_set(memc, "key1", key_length, "value1", value_length, (time_t)0, (uint32_t)0); ASSERT_TRUE(kv->exists("key1") == status::NOT_FOUND); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->exists("key1") == status::OK); memcached_delete(memc, "key1", key_length, (time_t)0); } // TEST_F(CachingTest, NegativeTTL) { // ASSERT_TRUE(!(start("caching", // "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":-10,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", // \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + // "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"force_create\": 1}}") == // status::OK); //} TEST_F(CachingTest, LargeTTL) { ASSERT_TRUE(start( "caching", "{\"host\":\"127.0.0.1\",\"port\":6379,\"attempts\":5,\"ttl\":999999999,\"path\":\"/dev/shm/pmemkv\",\"remote_type\":\"Redis\",\"remote_user\":\"xxx\", \"remote_pwd\":\"yyy\", \"remote_url\":\"...\", \"subengine\":\"" + ENGINE + "\",\"subengine_config\":{\"path\":\"" + PATH + "\", \"size\": 1073741824, \"force_create\": 1}}")); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); sleep(1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); sleep(1); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->exists("key1") == status::OK); } pmemkv-1.1/tests/engines-experimental/stree_pmemobj_test.cc000066400000000000000000000727361361504041500243560ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/engines-experimental/stree.h" #include "../../src/libpmemkv.hpp" #include "../../src/pmemobj_engine.h" #include "gtest/gtest.h" #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; class STreePmemobjTest : public testing::Test { public: std::string PATH = test_path + "/stree_pmemobj_test"; db *kv; STreePmemobjTest() { std::remove(PATH.c_str()); Start(true); } ~STreePmemobjTest() { delete kv; pmpool.close(); std::remove(PATH.c_str()); } void Restart() { delete kv; pmpool.close(); Start(false); } protected: void Start(bool create) { config cfg; if (create) { auto pop = pmem::obj::pool::create( PATH.c_str(), "STreePmemobjTest", SIZE, S_IRWXU); pmpool = pop; } else { auto pop = pmem::obj::pool::open(PATH.c_str(), "STreePmemobjTest"); pmpool = pop; } auto cfg_o = cfg.put_object("oid", &(pmpool.root()->oid), nullptr); if (cfg_o != status::OK) throw std::runtime_error("putting 'oid' to config failed"); kv = new db; auto s = kv->open("stree", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } struct Root { PMEMoid oid; }; pmem::obj::pool pmpool; }; TEST_F(STreePmemobjTest, SimpleTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(STreePmemobjTest, BinaryKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(STreePmemobjTest, BinaryValueTest) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(STreePmemobjTest, EmptyKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(STreePmemobjTest, EmptyValueTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(STreePmemobjTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(STreePmemobjTest, GetHeadlessTest) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, GetMultipleTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(STreePmemobjTest, GetMultiple2Test) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(STreePmemobjTest, GetNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, PutTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(STreePmemobjTest, PutKeysOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(STreePmemobjTest, PutValuesOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(STreePmemobjTest, RemoveAllTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveAndInsertTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveExistingTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(STreePmemobjTest, RemoveHeadlessTest) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(STreePmemobjTest, UsesGetAllTest) { ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); } // ============================================================================================= // TEST RECOVERY OF SINGLE-LEAF TREE // ============================================================================================= TEST_F(STreePmemobjTest, GetHeadlessAfterRecoveryTest) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, GetMultipleAfterRecoveryTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(STreePmemobjTest, GetMultiple2AfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(STreePmemobjTest, GetNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, PutAfterRecoveryTest) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(STreePmemobjTest, RemoveAllAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveAndInsertAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveExistingAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(STreePmemobjTest, RemoveHeadlessAfterRecoveryTest) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(STreePmemobjTest, RemoveNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } // ============================================================================================= // TEST TREE WITH SINGLE INNER NODE // ============================================================================================= const size_t INNER_ENTRIES = internal::stree::DEGREE - 1; const size_t LEAF_ENTRIES = internal::stree::DEGREE - 1; const size_t SINGLE_INNER_LIMIT = LEAF_ENTRIES * (INNER_ENTRIES - 1); TEST_F(STreePmemobjTest, SingleInnerNodeAscendingTest) { for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeAscendingTest2) { for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeDescendingTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeDescendingTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST RECOVERY OF TREE WITH SINGLE INNER NODE // ============================================================================================= TEST_F(STreePmemobjTest, SingleInnerNodeAscendingAfterRecoveryTest) { for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeAscendingAfterRecoveryTest2) { for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeDescendingAfterRecoveryTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, SingleInnerNodeDescendingAfterRecoveryTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreePmemobjTest, TransactionTest) { std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->put("key1", "value1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->get("key1", &value) == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK) << errormsg(); ASSERT_TRUE(value == "value1") << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->remove("key1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->remove("key1") == status::OK) << errormsg(); } pmemkv-1.1/tests/engines-experimental/stree_test.cc000066400000000000000000001432661361504041500226420ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/engines-experimental/stree.h" #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; const size_t LARGE_SIZE = 1024ull * 1024ull * 1024ull * 2ull; template class STreeBaseTest : public testing::Test { public: std::string PATH = test_path + "/stree_test"; db *kv; STreeBaseTest() { std::remove(PATH.c_str()); Start(true); } ~STreeBaseTest() { kv->close(); delete kv; std::remove(PATH.c_str()); } void Restart() { kv->close(); delete kv; Start(false); } protected: void Start(bool create) { config cfg; auto cfg_s = cfg.put_string("path", PATH); if (cfg_s != status::OK) throw std::runtime_error("putting 'path' to config failed"); if (create) { cfg_s = cfg.put_uint64("force_create", 1); if (cfg_s != status::OK) throw std::runtime_error( "putting 'force_create' to config failed"); cfg_s = cfg.put_int64("size", POOL_SIZE); if (cfg_s != status::OK) throw std::runtime_error( "putting 'size' to config failed"); } kv = new db; auto s = kv->open("stree", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } }; typedef STreeBaseTest STreeTest; typedef STreeBaseTest STreeLargeTest; TEST_F(STreeTest, SimpleTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(STreeTest, BinaryKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(STreeTest, BinaryValueTest) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(STreeTest, EmptyKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(STreeTest, EmptyValueTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(STreeTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(STreeTest, GetHeadlessTest) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreeTest, GetMultipleTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(STreeTest, GetMultiple2Test) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(STreeTest, GetNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreeTest, PutTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(STreeTest, PutKeysOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(STreeTest, PutValuesOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(STreeTest, RemoveAllTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(STreeTest, RemoveAndInsertTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(STreeTest, RemoveExistingTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(STreeTest, RemoveHeadlessTest) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(STreeTest, RemoveNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(STreeTest, UsesGetAllTest) { ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); } TEST_F(STreeTest, UsesGetAboveTest) { ASSERT_TRUE(kv->put("aaa", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("bbb", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ccc", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("rrr", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("sss", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ttt", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("yyy", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_above("ccc", cnt) == status::OK); ASSERT_EQ(4, cnt); std::string result; kv->get_above( "ccc", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_above("a", cnt) == status::OK); ASSERT_EQ(7, cnt); result.clear(); kv->get_above( "a", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_above("ddd", cnt) == status::OK); ASSERT_EQ(4, cnt); result.clear(); kv->get_above( "ddd", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_above("z", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_above( "z", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); } TEST_F(STreeTest, UsesGetEqualAboveTest) { ASSERT_TRUE(kv->put("aaa", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("bbb", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ccc", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("rrr", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("sss", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ttt", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("yyy", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("", cnt) == status::OK); ASSERT_EQ(7, cnt); std::string result; kv->get_equal_above( "", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("ccc", cnt) == status::OK); ASSERT_EQ(5, cnt); result.clear(); kv->get_equal_above( "ccc", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("a", cnt) == status::OK); ASSERT_EQ(7, cnt); result.clear(); kv->get_equal_above( "a", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("ddd", cnt) == status::OK); ASSERT_EQ(4, cnt); result.clear(); kv->get_equal_above( "ddd", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("x", cnt) == status::OK); ASSERT_EQ(1, cnt); result.clear(); kv->get_equal_above( "x", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("yyy", cnt) == status::OK); ASSERT_EQ(1, cnt); result.clear(); kv->get_equal_above( "yyy", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("z", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_equal_above( "z", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); } TEST_F(STreeTest, UsesGetEqualBelowTest) { ASSERT_TRUE(kv->put("aaa", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("bbb", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ccc", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("rrr", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("sss", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ttt", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("yyy", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("yyy", cnt) == status::OK); ASSERT_EQ(7, cnt); std::string result; kv->get_equal_below( "yyy", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("ttt", cnt) == status::OK); ASSERT_EQ(6, cnt); result.clear(); kv->get_equal_below( "ttt", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("ccc", cnt) == status::OK); ASSERT_EQ(3, cnt); result.clear(); kv->get_equal_below( "ccc", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("z", cnt) == status::OK); ASSERT_EQ(7, cnt); result.clear(); kv->get_equal_below( "z", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("ddd", cnt) == status::OK); ASSERT_EQ(3, cnt); result.clear(); kv->get_equal_below( "ddd", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("a", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_equal_below( "a", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_equal_below( "", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); } TEST_F(STreeTest, UsesGetBelowTest) { ASSERT_TRUE(kv->put("aaa", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("bbb", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ccc", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("rrr", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("sss", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ttt", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("yyy", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("a", cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string result; kv->get_below( "a", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("aaa", cnt) == status::OK); ASSERT_TRUE(cnt == 0); result.clear(); kv->get_below( "aaa", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("ccc", cnt) == status::OK); ASSERT_TRUE(cnt == 2); result.clear(); kv->get_below( "ccc", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("ddd", cnt) == status::OK); ASSERT_EQ(3, cnt); result.clear(); kv->get_below( "ddd", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("x", cnt) == status::OK); ASSERT_EQ(6, cnt); result.clear(); kv->get_below( "x", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("yyy", cnt) == status::OK); ASSERT_EQ(6, cnt); result.clear(); kv->get_below( "yyy", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_below("z", cnt) == status::OK); ASSERT_EQ(7, cnt); result.clear(); kv->get_below( "z", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); } TEST_F(STreeTest, UsesGetBetweenTest) { ASSERT_TRUE(kv->put("aaa", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("bbb", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ccc", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("rrr", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("sss", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("ttt", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("yyy", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("", "rrr", cnt) == status::OK); ASSERT_EQ(3, cnt); std::string result; kv->get_between( "", "rrr", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<1>|,<2>|,<3>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("ccc", "ttt", cnt) == status::OK); ASSERT_EQ(2, cnt); result.clear(); kv->get_between( "ccc", "ttt", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<4>|,<5>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("ddd", "x", cnt) == status::OK); ASSERT_EQ(3, cnt); result.clear(); kv->get_between( "ddd", "x", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<4>|,<5>|,<6>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("aaa", "yyy", cnt) == status::OK); ASSERT_EQ(5, cnt); result.clear(); kv->get_between( "aaa", "yyy", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == ",<2>|,<3>|,<4>|,<5>|,<6>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("yyy", "zzz", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_between( "yyy", "zzz", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("", "zzz", cnt) == status::OK); ASSERT_EQ(7, cnt); result.clear(); kv->get_between( "", "zzz", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE( result == ",<1>|,<2>|,<3>|,<4>|,<5>|,<6>|,<è®°!>|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_between("", "", cnt) == status::OK); ASSERT_EQ(0, cnt); result.clear(); kv->get_between( "", "", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result.empty()); } // ============================================================================================= // TEST RECOVERY OF SINGLE-LEAF TREE // ============================================================================================= TEST_F(STreeTest, GetHeadlessAfterRecoveryTest) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreeTest, GetMultipleAfterRecoveryTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(STreeTest, GetMultiple2AfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(STreeTest, GetNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(STreeTest, PutAfterRecoveryTest) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(STreeTest, RemoveAllAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(STreeTest, RemoveAndInsertAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(STreeTest, RemoveExistingAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(STreeTest, RemoveHeadlessAfterRecoveryTest) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(STreeTest, RemoveNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } // ============================================================================================= // TEST TREE WITH SINGLE INNER NODE // ============================================================================================= const size_t INNER_ENTRIES = internal::stree::DEGREE - 1; const size_t LEAF_ENTRIES = internal::stree::DEGREE - 1; const size_t SINGLE_INNER_LIMIT = LEAF_ENTRIES * (INNER_ENTRIES - 1); TEST_F(STreeTest, SingleInnerNodeAscendingTest) { for (std::size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (std::size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeAscendingTest2) { for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeDescendingTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeDescendingTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST RECOVERY OF TREE WITH SINGLE INNER NODE // ============================================================================================= TEST_F(STreeTest, SingleInnerNodeAscendingAfterRecoveryTest) { for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (size_t i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeAscendingAfterRecoveryTest2) { for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (size_t i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeDescendingAfterRecoveryTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(STreeTest, SingleInnerNodeDescendingAfterRecoveryTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST LARGE TREE // ============================================================================================= const int LARGE_LIMIT = 4000000; TEST_F(STreeLargeTest, LargeAscendingTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(STreeLargeTest, LargeDescendingTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } // ============================================================================================= // TEST RECOVERY OF LARGE TREE // ============================================================================================= TEST_F(STreeLargeTest, LargeAscendingAfterRecoveryTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); } Restart(); for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(STreeLargeTest, LargeDescendingAfterRecoveryTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); } Restart(); for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } pmemkv-1.1/tests/engines-experimental/tree3_pmemobj_test.cc000066400000000000000000000731021361504041500242420ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/engines-experimental/tree3.h" #include "../../src/libpmemkv.hpp" #include "../../src/pmemobj_engine.h" #include "../mock_tx_alloc.h" #include "gtest/gtest.h" #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; class TreePmemobjTest : public testing::Test { public: std::string PATH = test_path + "/tree3_pmemobj_test"; db *kv; TreePmemobjTest() { std::remove(PATH.c_str()); Start(true); } ~TreePmemobjTest() { delete kv; pmpool.close(); std::remove(PATH.c_str()); } void Restart() { delete kv; pmpool.close(); Start(false); } protected: void Start(bool create) { config cfg; if (create) { auto pop = pmem::obj::pool::create( PATH.c_str(), "TreePmemobjTest", SIZE, S_IRWXU); pmpool = pop; } else { auto pop = pmem::obj::pool::open(PATH.c_str(), "TreePmemobjTest"); pmpool = pop; } auto cfg_o = cfg.put_object("oid", &(pmpool.root()->oid), nullptr); if (cfg_o != status::OK) throw std::runtime_error("putting 'oid' to config failed"); kv = new db; auto s = kv->open("tree3", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } struct Root { PMEMoid oid; }; pmem::obj::pool pmpool; }; // ============================================================================================= // TEST SINGLE-LEAF TREE // ============================================================================================= TEST_F(TreePmemobjTest, SimpleTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(TreePmemobjTest, BinaryKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(TreePmemobjTest, BinaryValueTest) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(TreePmemobjTest, EmptyKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(TreePmemobjTest, EmptyValueTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(TreePmemobjTest, GetClearExternalValueTest) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(TreePmemobjTest, GetHeadlessTest) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, GetMultipleTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(TreePmemobjTest, GetMultiple2Test) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(TreePmemobjTest, GetNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, PutTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(TreePmemobjTest, PutKeysOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(TreePmemobjTest, PutValuesOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(TreePmemobjTest, RemoveAllTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveAndInsertTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveExistingTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(TreePmemobjTest, RemoveHeadlessTest) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(TreePmemobjTest, UsesGetAllTest) { ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all([&result](string_view k, string_view v) { result.append("<"); result.append(k.data(), k.size()); result.append(">,<"); result.append(v.data(), v.size()); result.append(">|"); return 0; }); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); } // ============================================================================================= // TEST RECOVERY OF SINGLE-LEAF TREE // ============================================================================================= TEST_F(TreePmemobjTest, GetHeadlessAfterRecoveryTest) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, GetMultipleAfterRecoveryTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(TreePmemobjTest, GetMultiple2AfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(TreePmemobjTest, GetNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, PutAfterRecoveryTest) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(TreePmemobjTest, RemoveAllAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveAndInsertAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveExistingAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(TreePmemobjTest, RemoveHeadlessAfterRecoveryTest) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(TreePmemobjTest, RemoveNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } // ============================================================================================= // TEST TREE WITH SINGLE INNER NODE // ============================================================================================= const int SINGLE_INNER_LIMIT = LEAF_KEYS * (INNER_KEYS - 1); TEST_F(TreePmemobjTest, SingleInnerNodeAscendingTest) { for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeAscendingTest2) { for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeDescendingTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeDescendingTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST RECOVERY OF TREE WITH SINGLE INNER NODE // ============================================================================================= TEST_F(TreePmemobjTest, SingleInnerNodeAscendingAfterRecoveryTest) { for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeAscendingAfterRecoveryTest2) { for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeDescendingAfterRecoveryTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, SingleInnerNodeDescendingAfterRecoveryTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreePmemobjTest, TransactionTest) { std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->put("key1", "value1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->get("key1", &value) == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK) << errormsg(); ASSERT_TRUE(value == "value1") << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->remove("key1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->remove("key1") == status::OK) << errormsg(); } pmemkv-1.1/tests/engines-experimental/tree3_test.cc000066400000000000000000001140231361504041500225270ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/engines-experimental/tree3.h" #include "../../src/libpmemkv.hpp" #include "../mock_tx_alloc.h" #include "gtest/gtest.h" using namespace pmem::kv; extern std::string test_path; const size_t SIZE = ((size_t)(1024 * 1024 * 1104)); config getConfig(const std::string &path, size_t size, bool create = true) { config cfg; auto cfg_s = cfg.put_string("path", path); if (cfg_s != status::OK) throw std::runtime_error("putting 'path' to config failed"); if (create) { cfg_s = cfg.put_uint64("force_create", 1); if (cfg_s != status::OK) throw std::runtime_error( "putting 'force_create' to config failed"); cfg_s = cfg.put_uint64("size", size); if (cfg_s != status::OK) throw std::runtime_error("putting 'size' to config failed"); } return cfg; } class TreeEmptyTest : public testing::Test { public: std::string PATH = test_path + "/tree3_empty_test"; TreeEmptyTest() { std::remove(PATH.c_str()); } }; class TreeTest : public testing::Test { public: std::string PATH = test_path + "/tree3_test"; const std::string PATH_CACHED = test_path + "/tree3_cached_test"; db *kv; TreeTest() { std::remove(PATH.c_str()); Start(true); } ~TreeTest() { kv->close(); delete kv; std::remove(PATH.c_str()); } void Restart() { kv->close(); delete kv; Start(false); } protected: void Start(bool create) { kv = new db; auto s = kv->open("tree3", getConfig(PATH, SIZE, create)); if (s != status::OK) throw std::runtime_error(errormsg()); } }; // ============================================================================================= // TEST EMPTY TREE // ============================================================================================= TEST_F(TreeEmptyTest, CreateInstanceTest) { db *kv = new db; auto s = kv->open("tree3", getConfig(PATH, PMEMOBJ_MIN_POOL)); if (s != status::OK) throw std::runtime_error(errormsg()); delete kv; } struct Context { int64_t count; }; TEST_F(TreeEmptyTest, CreateInstanceWithContextTest) { db *kv = new db; auto s = kv->open("tree3", getConfig(PATH, PMEMOBJ_MIN_POOL)); if (s != status::OK) throw std::runtime_error("Open failed"); delete kv; } TEST_F(TreeEmptyTest, FailsToCreateInstanceWithInvalidPath) { try { auto kv = new db; auto s = kv->open("tree3", getConfig("/tmp/123/234/345/456/567/678/nope.nope", PMEMOBJ_MIN_POOL)); if (s != status::OK) throw std::runtime_error(errormsg()); FAIL(); } catch (...) { // do nothing, expected to happen } } TEST_F(TreeEmptyTest, FailsToCreateInstanceWithHugeSize) { try { auto kv = new db; auto s = kv->open("tree3", getConfig(PATH, 9223372036854775807)); // 9.22 exabytes if (s != status::OK) throw std::runtime_error(errormsg()); FAIL(); } catch (...) { // do nothing, expected to happen } } TEST_F(TreeEmptyTest, FailsToCreateInstanceWithTinySize) { try { auto kv = new db; auto s = kv->open("tree3", getConfig(PATH, PMEMOBJ_MIN_POOL - 1)); // too small if (s != status::OK) throw std::runtime_error(errormsg()); FAIL(); } catch (...) { // do nothing, expected to happen } } // ============================================================================================= // TEST SINGLE-LEAF TREE // ============================================================================================= TEST_F(TreeTest, SimpleTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(TreeTest, BinaryKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(TreeTest, BinaryValueTest) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(TreeTest, EmptyKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(TreeTest, EmptyValueTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(TreeTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(TreeTest, GetHeadlessTest) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreeTest, GetMultipleTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(TreeTest, GetMultiple2Test) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(TreeTest, GetNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreeTest, PutTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(TreeTest, PutKeysOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(TreeTest, PutValuesOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(TreeTest, RemoveAllTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(TreeTest, RemoveAndInsertTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(TreeTest, RemoveExistingTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(TreeTest, RemoveHeadlessTest) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(TreeTest, RemoveNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(TreeTest, UsesGetAllTest) { ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all([&result](string_view k, string_view v) { result.append("<"); result.append(k.data(), k.size()); result.append(">,<"); result.append(v.data(), v.size()); result.append(">|"); return 0; }); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); } // ============================================================================================= // TEST RECOVERY OF SINGLE-LEAF TREE // ============================================================================================= TEST_F(TreeTest, GetHeadlessAfterRecoveryTest) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreeTest, GetMultipleAfterRecoveryTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(TreeTest, GetMultiple2AfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(TreeTest, GetNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(TreeTest, PutAfterRecoveryTest) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(TreeTest, RemoveAllAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(TreeTest, RemoveAndInsertAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(TreeTest, RemoveExistingAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(TreeTest, RemoveHeadlessAfterRecoveryTest) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(TreeTest, RemoveNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } // ============================================================================================= // TEST TREE WITH SINGLE INNER NODE // ============================================================================================= const int SINGLE_INNER_LIMIT = LEAF_KEYS * (INNER_KEYS - 1); TEST_F(TreeTest, SingleInnerNodeAscendingTest) { for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeAscendingTest2) { for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeDescendingTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeDescendingTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST RECOVERY OF TREE WITH SINGLE INNER NODE // ============================================================================================= TEST_F(TreeTest, SingleInnerNodeAscendingAfterRecoveryTest) { for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = 10000; i < (10000 + SINGLE_INNER_LIMIT); i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeAscendingAfterRecoveryTest2) { for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = 0; i < SINGLE_INNER_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeDescendingAfterRecoveryTest) { for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = (10000 + SINGLE_INNER_LIMIT); i > 10000; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } TEST_F(TreeTest, SingleInnerNodeDescendingAfterRecoveryTest2) { for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, istr) == status::OK) << errormsg(); } Restart(); for (int i = SINGLE_INNER_LIMIT; i > 0; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == istr); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == SINGLE_INNER_LIMIT); } // ============================================================================================= // TEST LARGE TREE // ============================================================================================= const int LARGE_LIMIT = 4000000; TEST_F(TreeTest, LargeAscendingTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(TreeTest, LargeDescendingTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } // ============================================================================================= // TEST RECOVERY OF LARGE TREE // ============================================================================================= TEST_F(TreeTest, LargeAscendingAfterRecoveryTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); } Restart(); for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(TreeTest, LargeDescendingAfterRecoveryTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); } Restart(); for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } // ============================================================================================= // TEST RUNNING OUT OF SPACE // ============================================================================================= class TreeFullTest : public testing::Test { public: std::string PATH = test_path + "/tree3_full_test"; const std::string PATH_CACHED = test_path + "/tree3_full_cached_test"; db *kv; TreeFullTest() { std::remove(PATH.c_str()); Start(); } ~TreeFullTest() { delete kv; } void Restart() { delete kv; kv = new db; auto s = kv->open("tree3", getConfig(PATH, SIZE, true)); if (s != status::OK) throw std::runtime_error(errormsg()); } void Validate() { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } Restart(); ASSERT_TRUE(kv->put("1", "!1") == status::OK); std::string value; ASSERT_TRUE(kv->get("1", &value) == status::OK && value == ("!1")); ASSERT_TRUE(kv->put("1", "1!") == status::OK); std::string value2; ASSERT_TRUE(kv->get("1", &value2) == status::OK && value2 == ("1!")); for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value3; ASSERT_TRUE(kv->get(istr, &value3) == status::OK && value3 == (istr + "!")); } } private: void Start() { if (access(PATH_CACHED.c_str(), F_OK) == 0) { ASSERT_TRUE(std::system(("cp -f " + PATH_CACHED + " " + PATH) .c_str()) == 0); } else { std::cout << "!!! creating cached copy at " << PATH_CACHED << "\n"; db *kvt = new db; auto s = kvt->open("tree3", getConfig(PATH, SIZE)); if (s != status::OK) throw std::runtime_error(errormsg()); for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kvt->put(istr, (istr + "!")) == status::OK) << errormsg(); } delete kvt; ASSERT_TRUE(std::system(("cp -f " + PATH + " " + PATH_CACHED) .c_str()) == 0); } kv = new db; auto s = kv->open("tree3", getConfig(PATH, SIZE)); if (s != status::OK) throw std::runtime_error(errormsg()); } }; const std::string LONGSTR = "123456789A123456789A123456789A123456789A123456789A123456789A123456789A"; TEST_F(TreeFullTest, OutOfSpace1Test) { tx_alloc_should_fail = true; ASSERT_TRUE(kv->put("100", "?") != status::OK); tx_alloc_should_fail = false; Validate(); } TEST_F(TreeFullTest, OutOfSpace2aTest) { ASSERT_TRUE(kv->remove("100") == status::OK); tx_alloc_should_fail = true; ASSERT_TRUE(kv->put("100", LONGSTR) != status::OK); tx_alloc_should_fail = false; ASSERT_TRUE(kv->put("100", "100!") == status::OK) << errormsg(); Validate(); } TEST_F(TreeFullTest, OutOfSpace2bTest) { ASSERT_TRUE(kv->remove("100") == status::OK); ASSERT_TRUE(kv->put("100", "100!") == status::OK) << errormsg(); tx_alloc_should_fail = true; ASSERT_TRUE(kv->put("100", LONGSTR) != status::OK); tx_alloc_should_fail = false; Validate(); } TEST_F(TreeFullTest, OutOfSpace3aTest) { tx_alloc_should_fail = true; ASSERT_TRUE(kv->put("100", LONGSTR) != status::OK); tx_alloc_should_fail = false; Validate(); } TEST_F(TreeFullTest, OutOfSpace3bTest) { tx_alloc_should_fail = true; for (int i = 0; i <= 99999; i++) { ASSERT_TRUE(kv->put("123456", LONGSTR) != status::OK); } tx_alloc_should_fail = false; ASSERT_TRUE(kv->remove("4567") == status::OK); ASSERT_TRUE(kv->put("4567", "4567!") == status::OK) << errormsg(); Validate(); } TEST_F(TreeFullTest, OutOfSpace4aTest) { tx_alloc_should_fail = true; ASSERT_TRUE(kv->put(std::to_string(LARGE_LIMIT + 1), "1") != status::OK); tx_alloc_should_fail = false; Validate(); } TEST_F(TreeFullTest, OutOfSpace4bTest) { tx_alloc_should_fail = true; for (int i = 0; i <= 99999; i++) { ASSERT_TRUE(kv->put(std::to_string(LARGE_LIMIT + 1), "1") != status::OK); } tx_alloc_should_fail = false; ASSERT_TRUE(kv->remove("98765") == status::OK); ASSERT_TRUE(kv->put("98765", "98765!") == status::OK) << errormsg(); Validate(); } TEST_F(TreeFullTest, OutOfSpace5aTest) { tx_alloc_should_fail = true; ASSERT_TRUE(kv->put(LONGSTR, "1") != status::OK); ASSERT_TRUE(kv->put(LONGSTR, LONGSTR) != status::OK); tx_alloc_should_fail = false; Validate(); } TEST_F(TreeFullTest, OutOfSpace5bTest) { tx_alloc_should_fail = true; for (int i = 0; i <= 99999; i++) { ASSERT_TRUE(kv->put(LONGSTR, "1") != status::OK); ASSERT_TRUE(kv->put(LONGSTR, LONGSTR) != status::OK); } tx_alloc_should_fail = false; ASSERT_TRUE(kv->remove("34567") == status::OK); ASSERT_TRUE(kv->put("34567", "34567!") == status::OK) << errormsg(); Validate(); } TEST_F(TreeFullTest, OutOfSpace6Test) { tx_alloc_should_fail = true; ASSERT_TRUE(kv->put(LONGSTR, "?") != status::OK); tx_alloc_should_fail = false; std::string str; ASSERT_TRUE(kv->get(LONGSTR, &str) == status::NOT_FOUND); Validate(); } TEST_F(TreeFullTest, RepeatedRecoveryTest) { for (int i = 1; i <= 100; i++) Restart(); Validate(); } pmemkv-1.1/tests/engines/000077500000000000000000000000001361504041500154435ustar00rootroot00000000000000pmemkv-1.1/tests/engines/blackhole_test.cc000066400000000000000000000130461361504041500207410ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" using namespace pmem::kv; class BlackholeTest : public testing::Test { public: db kv; BlackholeTest() { auto s = kv.open("blackhole"); if (s != status::OK) throw std::runtime_error(errormsg()); } ~BlackholeTest() { kv.close(); } }; TEST_F(BlackholeTest, SimpleTest_TRACERS_MP) { std::string value; std::size_t cnt = 1; ASSERT_TRUE(kv.count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv.get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv.put("key1", "value1") == status::OK); cnt = 1; ASSERT_TRUE(kv.count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv.get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv.remove("key1") == status::OK); ASSERT_TRUE(kv.get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv.defrag() == status::NOT_SUPPORTED); } TEST_F(BlackholeTest, GetRangeTest_TRACERS_MP) { std::string result; std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv.put("key1", "value1") == status::OK); ASSERT_TRUE(kv.put("key2", "value2") == status::OK); ASSERT_TRUE(kv.put("key3", "value3") == status::OK); ASSERT_TRUE(kv.count_above("key1", cnt) == status::OK); ASSERT_EQ(0, cnt); ASSERT_TRUE(kv.get_above( "key1", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result) == status::NOT_FOUND); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv.count_equal_above("key1", cnt) == status::OK); ASSERT_EQ(0, cnt); ASSERT_TRUE(kv.get_equal_above( "key1", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result) == status::NOT_FOUND); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv.count_below("key1", cnt) == status::OK); ASSERT_EQ(0, cnt); ASSERT_TRUE(kv.get_below( "key1", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result) == status::NOT_FOUND); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv.count_equal_below("key1", cnt) == status::OK); ASSERT_EQ(0, cnt); ASSERT_TRUE(kv.get_equal_below( "key1", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result) == status::NOT_FOUND); ASSERT_TRUE(result.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv.count_between("", "key3", cnt) == status::OK); ASSERT_EQ(0, cnt); ASSERT_TRUE(kv.get_between( "", "key3", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result) == status::NOT_FOUND); ASSERT_TRUE(result.empty()); } /* XXX port it to other engines */ TEST_F(BlackholeTest, ErrormsgTest) { auto s = kv.open("non-existing name"); ASSERT_TRUE(s == pmem::kv::status::WRONG_ENGINE_NAME); auto err = pmem::kv::errormsg(); ASSERT_TRUE(err.size() > 0); s = kv.open("non-existing name"); ASSERT_TRUE(s == pmem::kv::status::WRONG_ENGINE_NAME); s = kv.open("non-existing name"); ASSERT_TRUE(s == pmem::kv::status::WRONG_ENGINE_NAME); /* Test whether errormsg is cleared correctly after each error */ ASSERT_TRUE(pmem::kv::errormsg() == err); } pmemkv-1.1/tests/engines/cmap_pmemobj_test.cc000066400000000000000000000627541361504041500214600ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "../../src/pmemobj_engine.h" #include "gtest/gtest.h" #include #include #include #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; class CMapPmemobjTest : public testing::Test { private: std::string PATH = test_path + "/cmap_pmemobj_test"; public: std::unique_ptr kv = nullptr; CMapPmemobjTest() { std::remove(PATH.c_str()); Start(true); } ~CMapPmemobjTest() { pmpool.close(); std::remove(PATH.c_str()); } void Restart() { kv.reset(nullptr); pmpool.close(); Start(false); } protected: void Start(bool create) { config cfg; if (create) { auto pop = pmem::obj::pool::create( PATH.c_str(), "CMapPmemobjTest", SIZE, S_IRWXU); pmpool = pop; } else { auto pop = pmem::obj::pool::open(PATH.c_str(), "CMapPmemobjTest"); pmpool = pop; } auto cfg_o = cfg.put_object("oid", &(pmpool.root()->oid), nullptr); if (cfg_o != status::OK) throw std::runtime_error("putting 'oid' to config failed"); kv.reset(new db); auto s = kv->open("cmap", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } struct Root { PMEMoid oid; }; pmem::obj::pool pmpool; }; // ============================================================================================= // TEST SMALL COLLECTIONS // ============================================================================================= TEST_F(CMapPmemobjTest, SimpleTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, BinaryKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, BinaryValueTest) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, EmptyKeyTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, EmptyValueTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetClearExternalValueTest) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetHeadlessTest) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetMultipleTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetMultiple2Test) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, PutTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, PutKeysOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, PutValuesOfDifferentSizesTest) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveAllTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveAndInsertTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveExistingTest) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveHeadlessTest) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveNonexistentTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, UsesGetAllTest) { ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); ASSERT_TRUE(kv->defrag() == status::OK); } // ============================================================================================= // TEST RECOVERY // ============================================================================================= TEST_F(CMapPmemobjTest, GetHeadlessAfterRecoveryTest) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetMultipleAfterRecoveryTest) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetMultiple2AfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, GetNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, PutAfterRecoveryTest) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveAllAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveAndInsertAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveExistingAfterRecoveryTest) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveHeadlessAfterRecoveryTest) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, RemoveNonexistentAfterRecoveryTest) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapPmemobjTest, TransactionTest) { std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->put("key1", "value1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->get("key1", &value) == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); value = ""; ASSERT_TRUE(kv->get("key1", &value) == status::OK) << errormsg(); ASSERT_TRUE(value == "value1") << errormsg(); pmem::obj::transaction::run(pmpool, [&] { ASSERT_TRUE(kv->remove("key1") == status::TRANSACTION_SCOPE_ERROR) << errormsg(); }); ASSERT_TRUE(kv->remove("key1") == status::OK) << errormsg(); ASSERT_TRUE(kv->defrag() == status::OK); } pmemkv-1.1/tests/engines/cmap_test.cc000066400000000000000000000724201361504041500177360ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" #include #include #include #include #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; const size_t LARGE_SIZE = 1024ull * 1024ull * 1024ull * 2ull; template void parallel_exec(size_t threads_number, Function f) { std::vector threads; threads.reserve(threads_number); for (size_t i = 0; i < threads_number; ++i) { threads.emplace_back(f, i); } for (auto &t : threads) { t.join(); } } template class CMapBaseTest : public testing::Test { private: std::string PATH = test_path + "/cmap_test"; public: std::unique_ptr kv = nullptr; CMapBaseTest() { std::remove(PATH.c_str()); Start(true); } ~CMapBaseTest() { kv->close(); std::remove(PATH.c_str()); } void Restart() { kv->close(); kv.reset(nullptr); Start(false); } protected: void Start(bool create) { config cfg; auto cfg_s = cfg.put_string("path", PATH); if (cfg_s != status::OK) throw std::runtime_error("putting 'path' to config failed"); if (create) { cfg_s = cfg.put_uint64("force_create", 1); if (cfg_s != status::OK) throw std::runtime_error( "putting 'force_create' to config failed"); cfg_s = cfg.put_uint64("size", POOL_SIZE); if (cfg_s != status::OK) throw std::runtime_error( "putting 'size' to config failed"); } kv.reset(new db); auto s = kv->open("cmap", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } }; using CMapTest = CMapBaseTest; using CMapLargeTest = CMapBaseTest; // ============================================================================================= // TEST SMALL COLLECTIONS // ============================================================================================= TEST_F(CMapTest, SimpleTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); ASSERT_TRUE(kv->defrag() == status::OK); ASSERT_TRUE(kv->defrag(50, 100) == status::INVALID_ARGUMENT); } TEST_F(CMapTest, BinaryKeyTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, BinaryValueTest_TRACERS_MPHD) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, EmptyKeyTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, EmptyValueTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetHeadlessTest_TRACERS_MPHD) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetMultipleTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetMultiple2Test_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetNonexistentTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, PutTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, PutKeysOfDifferentSizesTest_TRACERS_MPHD) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, PutValuesOfDifferentSizesTest_TRACERS_MPHD) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveAllTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveAndInsertTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveExistingTest_TRACERS_MPHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveHeadlessTest_TRACERS_MPHD) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveNonexistentTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, UsesGetAllTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, SimpleMultithreadedTest_TRACERS_MPHD) { size_t threads_number = 8; size_t thread_items = 50; parallel_exec(threads_number, [&](size_t thread_id) { size_t begin = thread_id * thread_items; size_t end = begin + thread_items; for (auto i = begin; i < end; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (auto i = begin; i < end; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } }); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == threads_number * thread_items); ASSERT_TRUE(kv->defrag() == status::OK); } // ============================================================================================= // TEST RECOVERY // ============================================================================================= TEST_F(CMapTest, GetHeadlessAfterRecoveryTest_TRACERS_MPHD) { Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetMultipleAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetMultiple2AfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); Restart(); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, GetNonexistentAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, PutAfterRecoveryTest_TRACERS_MPHD) { std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); Restart(); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveAllAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveAndInsertAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveExistingAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); Restart(); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveHeadlessAfterRecoveryTest_TRACERS_MPHD) { Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapTest, RemoveNonexistentAfterRecoveryTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); Restart(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(kv->defrag() == status::OK); } // ============================================================================================= // TEST LARGE COLLECTIONS // ============================================================================================= const int LARGE_LIMIT = 4000000; TEST_F(CMapLargeTest, LargeAscendingTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapLargeTest, LargeAscendingAfterRecoveryTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } Restart(); for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapLargeTest, LargeDescendingTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); ASSERT_TRUE(kv->defrag() == status::OK); } TEST_F(CMapLargeTest, LargeDescendingAfterRecoveryTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } Restart(); for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); ASSERT_TRUE(kv->defrag() == status::OK); } /* XXX port it to other engines */ TEST_F(CMapTest, NullConfigTest) { config empty_c_cfg; auto s = kv->open("cmap", std::move(empty_c_cfg)); ASSERT_TRUE(s == status::INVALID_ARGUMENT) << errormsg(); } pmemkv-1.1/tests/engines/vcmap_test.cc000066400000000000000000000502721361504041500201250ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" #include #include #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; const size_t LARGE_SIZE = 1024ull * 1024ull * 1024ull * 2ull; template class VCMapBaseTest : public testing::Test { private: std::string PATH = test_path + "/vcmap_test"; public: std::unique_ptr kv = nullptr; VCMapBaseTest() { mkdir(PATH.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); config cfg; auto cfg_s = cfg.put_string("path", PATH); if (cfg_s != status::OK) throw std::runtime_error("putting 'path' to config failed"); cfg_s = cfg.put_int64("size", POOL_SIZE); if (cfg_s != status::OK) throw std::runtime_error("putting 'size' to config failed"); kv.reset(new db); auto s = kv->open("vcmap", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } ~VCMapBaseTest() { kv->close(); std::remove(PATH.c_str()); } }; using VCMapTest = VCMapBaseTest; using VCMapLargeTest = VCMapBaseTest; // ============================================================================================= // TEST SMALL COLLECTIONS // ============================================================================================= TEST_F(VCMapTest, SimpleTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); } TEST_F(VCMapTest, BinaryKeyTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(VCMapTest, BinaryValueTest_TRACERS_MHD) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(VCMapTest, EmptyKeyTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(VCMapTest, EmptyValueTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(VCMapTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(VCMapTest, GetHeadlessTest_TRACERS_MHD) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(VCMapTest, GetMultipleTest_TRACERS_MHD) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(VCMapTest, GetMultiple2Test_TRACERS_MHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(VCMapTest, GetNonexistentTest_TRACERS_MHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(VCMapTest, PutTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(VCMapTest, PutKeysOfDifferentSizesTest_TRACERS_MHD) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(VCMapTest, PutValuesOfDifferentSizesTest_TRACERS_MHD) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(VCMapTest, RemoveAllTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(VCMapTest, RemoveAndInsertTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(VCMapTest, RemoveExistingTest_TRACERS_MHD) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(VCMapTest, RemoveHeadlessTest_TRACERS_MHD) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(VCMapTest, RemoveNonexistentTest_TRACERS_MHD) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(VCMapTest, UsesGetAllTest_TRACERS_MHD) { ASSERT_TRUE(kv->put("1", "2") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("RR", "è®°!") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string result; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<"); c->append(std::string(k, kb)); c->append(">,<"); c->append(std::string(v, vb)); c->append(">|"); return 0; }, &result); ASSERT_TRUE(result == "<1>,<2>|,<è®°!>|"); } // ============================================================================================= // TEST LARGE COLLECTIONS // ============================================================================================= const int LARGE_LIMIT = 4000000; TEST_F(VCMapLargeTest, LargeAscendingTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(VCMapLargeTest, LargeDescendingTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } pmemkv-1.1/tests/engines/vsmap_test.cc000066400000000000000000001036651361504041500201520ustar00rootroot00000000000000/* * Copyright 2017-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../../src/libpmemkv.hpp" #include "gtest/gtest.h" #include #include #include using namespace pmem::kv; extern std::string test_path; const size_t SIZE = 1024ull * 1024ull * 512ull; const size_t LARGE_SIZE = 1024ull * 1024ull * 1024ull * 2ull; template class VSMapBaseTest : public testing::Test { private: std::string PATH = test_path + "/vsmap_test"; public: std::unique_ptr kv = nullptr; VSMapBaseTest() { mkdir(PATH.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); config cfg; auto cfg_s = cfg.put_string("path", PATH); if (cfg_s != status::OK) throw std::runtime_error("putting 'path' to config failed"); cfg_s = cfg.put_int64("size", POOL_SIZE); if (cfg_s != status::OK) throw std::runtime_error("putting 'size' to config failed"); kv.reset(new db); auto s = kv->open("vsmap", std::move(cfg)); if (s != status::OK) throw std::runtime_error(errormsg()); } ~VSMapBaseTest() { kv->close(); std::remove(PATH.c_str()); } }; using VSMapTest = VSMapBaseTest; using VSMapLargeTest = VSMapBaseTest; // ============================================================================================= // TEST SMALL COLLECTIONS // ============================================================================================= TEST_F(VSMapTest, SimpleTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("key1")); std::string value; ASSERT_TRUE(kv->get("key1", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("key1")); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); value = ""; kv->get("key1", [&](string_view v) { value.append(v.data(), v.size()); }); ASSERT_TRUE(value == "value1"); ASSERT_TRUE(kv->defrag() == status::NOT_SUPPORTED); } TEST_F(VSMapTest, BinaryKeyTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("a")); ASSERT_TRUE(kv->put("a", "should_not_change") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); std::string key1 = std::string("a\0b", 3); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); ASSERT_TRUE(kv->put(key1, "stuff") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::OK == kv->exists(key1)); std::string value; ASSERT_TRUE(kv->get(key1, &value) == status::OK); ASSERT_EQ(value, "stuff"); std::string value2; ASSERT_TRUE(kv->get("a", &value2) == status::OK); ASSERT_EQ(value2, "should_not_change"); ASSERT_TRUE(kv->remove(key1) == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("a")); ASSERT_TRUE(status::NOT_FOUND == kv->exists(key1)); std::string value3; ASSERT_TRUE(kv->get(key1, &value3) == status::NOT_FOUND); ASSERT_TRUE(kv->get("a", &value3) == status::OK && value3 == "should_not_change"); } TEST_F(VSMapTest, BinaryValueTest_TRACERS_M) { std::string value("A\0B\0\0C", 6); ASSERT_TRUE(kv->put("key1", value) == status::OK) << errormsg(); std::string value_out; ASSERT_TRUE(kv->get("key1", &value_out) == status::OK && (value_out.length() == 6) && (value_out == value)); } TEST_F(VSMapTest, EmptyKeyTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("", "empty") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put(" ", "single-space") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("\t\t", "two-tab") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(status::OK == kv->exists("")); ASSERT_TRUE(kv->get("", &value1) == status::OK && value1 == "empty"); ASSERT_TRUE(status::OK == kv->exists(" ")); ASSERT_TRUE(kv->get(" ", &value2) == status::OK && value2 == "single-space"); ASSERT_TRUE(status::OK == kv->exists("\t\t")); ASSERT_TRUE(kv->get("\t\t", &value3) == status::OK && value3 == "two-tab"); } TEST_F(VSMapTest, EmptyValueTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("empty", "") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("single-space", " ") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->put("two-tab", "\t\t") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_TRUE(kv->get("empty", &value1) == status::OK && value1 == ""); ASSERT_TRUE(kv->get("single-space", &value2) == status::OK && value2 == " "); ASSERT_TRUE(kv->get("two-tab", &value3) == status::OK && value3 == "\t\t"); } TEST_F(VSMapTest, GetClearExternalValueTest_TRACERS_MPHD) { ASSERT_TRUE(kv->put("key1", "cool") == status::OK) << errormsg(); std::string value = "super"; ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "cool"); value = "super"; ASSERT_TRUE(kv->get("non_existent_key", &value) == status::NOT_FOUND && value == "super"); } TEST_F(VSMapTest, GetHeadlessTest_TRACERS_M) { ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(VSMapTest, GetMultipleTest_TRACERS_M) { ASSERT_TRUE(kv->put("abc", "A1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("def", "B2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("hij", "C3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("jkl", "D4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("mno", "E5") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(status::OK == kv->exists("abc")); std::string value1; ASSERT_TRUE(kv->get("abc", &value1) == status::OK && value1 == "A1"); ASSERT_TRUE(status::OK == kv->exists("def")); std::string value2; ASSERT_TRUE(kv->get("def", &value2) == status::OK && value2 == "B2"); ASSERT_TRUE(status::OK == kv->exists("hij")); std::string value3; ASSERT_TRUE(kv->get("hij", &value3) == status::OK && value3 == "C3"); ASSERT_TRUE(status::OK == kv->exists("jkl")); std::string value4; ASSERT_TRUE(kv->get("jkl", &value4) == status::OK && value4 == "D4"); ASSERT_TRUE(status::OK == kv->exists("mno")); std::string value5; ASSERT_TRUE(kv->get("mno", &value5) == status::OK && value5 == "E5"); } TEST_F(VSMapTest, GetMultiple2Test_TRACERS_M) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key2", "value2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("key3", "value3") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("key2") == status::OK); ASSERT_TRUE(kv->put("key3", "VALUE3") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); std::string value1; ASSERT_TRUE(kv->get("key1", &value1) == status::OK && value1 == "value1"); std::string value2; ASSERT_TRUE(kv->get("key2", &value2) == status::NOT_FOUND); std::string value3; ASSERT_TRUE(kv->get("key3", &value3) == status::OK && value3 == "VALUE3"); } TEST_F(VSMapTest, GetNonexistentTest_TRACERS_M) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(status::NOT_FOUND == kv->exists("waldo")); std::string value; ASSERT_TRUE(kv->get("waldo", &value) == status::NOT_FOUND); } TEST_F(VSMapTest, PutTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); std::string value; ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &value) == status::OK && value == "value1"); std::string new_value; ASSERT_TRUE(kv->put("key1", "VALUE1") == status::OK) << errormsg(); // same size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value) == status::OK && new_value == "VALUE1"); std::string new_value2; ASSERT_TRUE(kv->put("key1", "new_value") == status::OK) << errormsg(); // longer size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value2) == status::OK && new_value2 == "new_value"); std::string new_value3; ASSERT_TRUE(kv->put("key1", "?") == status::OK) << errormsg(); // shorter size cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("key1", &new_value3) == status::OK && new_value3 == "?"); } TEST_F(VSMapTest, PutKeysOfDifferentSizesTest_TRACERS_M) { std::string value; ASSERT_TRUE(kv->put("123456789ABCDE", "A") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("123456789ABCDE", &value) == status::OK && value == "A"); std::string value2; ASSERT_TRUE(kv->put("123456789ABCDEF", "B") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("123456789ABCDEF", &value2) == status::OK && value2 == "B"); std::string value3; ASSERT_TRUE(kv->put("12345678ABCDEFG", "C") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("12345678ABCDEFG", &value3) == status::OK && value3 == "C"); std::string value4; ASSERT_TRUE(kv->put("123456789", "D") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("123456789", &value4) == status::OK && value4 == "D"); std::string value5; ASSERT_TRUE(kv->put("123456789ABCDEFGHI", "E") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("123456789ABCDEFGHI", &value5) == status::OK && value5 == "E"); } TEST_F(VSMapTest, PutValuesOfDifferentSizesTest_TRACERS_M) { std::string value; ASSERT_TRUE(kv->put("A", "123456789ABCDE") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->get("A", &value) == status::OK && value == "123456789ABCDE"); std::string value2; ASSERT_TRUE(kv->put("B", "123456789ABCDEF") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->get("B", &value2) == status::OK && value2 == "123456789ABCDEF"); std::string value3; ASSERT_TRUE(kv->put("C", "12345678ABCDEFG") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->get("C", &value3) == status::OK && value3 == "12345678ABCDEFG"); std::string value4; ASSERT_TRUE(kv->put("D", "123456789") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 4); ASSERT_TRUE(kv->get("D", &value4) == status::OK && value4 == "123456789"); std::string value5; ASSERT_TRUE(kv->put("E", "123456789ABCDEFGHI") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 5); ASSERT_TRUE(kv->get("E", &value5) == status::OK && value5 == "123456789ABCDEFGHI"); } TEST_F(VSMapTest, RemoveAllTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); } TEST_F(VSMapTest, RemoveAndInsertTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey")); std::string value; ASSERT_TRUE(kv->get("tmpkey", &value) == status::NOT_FOUND); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::OK == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::OK && value == "tmpvalue1"); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); } TEST_F(VSMapTest, RemoveExistingTest_TRACERS_M) { std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->put("tmpkey1", "tmpvalue1") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->put("tmpkey2", "tmpvalue2") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->remove("tmpkey1") == status::OK); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->remove("tmpkey1") == status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(status::NOT_FOUND == kv->exists("tmpkey1")); std::string value; ASSERT_TRUE(kv->get("tmpkey1", &value) == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("tmpkey2")); ASSERT_TRUE(kv->get("tmpkey2", &value) == status::OK && value == "tmpvalue2"); } TEST_F(VSMapTest, RemoveHeadlessTest_TRACERS_M) { ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); } TEST_F(VSMapTest, RemoveNonexistentTest_TRACERS_M) { ASSERT_TRUE(kv->put("key1", "value1") == status::OK) << errormsg(); ASSERT_TRUE(kv->remove("nada") == status::NOT_FOUND); ASSERT_TRUE(status::OK == kv->exists("key1")); } TEST_F(VSMapTest, UsesCountTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BD", "7") == status::OK) << errormsg(); std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == 7); ASSERT_TRUE(kv->count_above("", cnt) == status::OK); ASSERT_TRUE(cnt == 7); ASSERT_TRUE(kv->count_above("A", cnt) == status::OK); ASSERT_TRUE(cnt == 6); ASSERT_TRUE(kv->count_above("B", cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->count_above("BC", cnt) == status::OK); ASSERT_TRUE(cnt == 1); ASSERT_TRUE(kv->count_above("BD", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_above("Z", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_below("", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_below("A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_below("B", cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->count_below("BD", cnt) == status::OK); ASSERT_TRUE(cnt == 6); ASSERT_TRUE(kv->count_below("ZZZZZ", cnt) == status::OK); ASSERT_TRUE(cnt == 7); ASSERT_TRUE(kv->count_between("", "ZZZZ", cnt) == status::OK); ASSERT_TRUE(cnt == 7); ASSERT_TRUE(kv->count_between("", "A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("", "B", cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->count_between("A", "B", cnt) == status::OK); ASSERT_TRUE(cnt == 2); ASSERT_TRUE(kv->count_between("B", "ZZZZ", cnt) == status::OK); ASSERT_TRUE(cnt == 3); ASSERT_TRUE(kv->count_between("", "", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("A", "A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("AC", "A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("B", "A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("BD", "A", cnt) == status::OK); ASSERT_TRUE(cnt == 0); ASSERT_TRUE(kv->count_between("ZZZ", "B", cnt) == status::OK); ASSERT_TRUE(cnt == 0); } TEST_F(VSMapTest, UsesGetAllTest_TRACERS_M) { ASSERT_TRUE(kv->put("1", "one") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("2", "two") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); std::string x; kv->get_all([&](string_view k, string_view v) { x.append("<") .append(k.data(), k.size()) .append(">,<") .append(v.data(), v.size()) .append(">|"); return 0; }); ASSERT_TRUE(x == "<1>,|<2>,|<è®°!>,|"); x = ""; kv->get_all([&](string_view k, string_view v) { x.append("<") .append(std::string(k.data(), k.size())) .append(">,<") .append(std::string(v.data(), v.size())) .append(">|"); return 0; }); ASSERT_TRUE(x == "<1>,|<2>,|<è®°!>,|"); x = ""; kv->get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append("<") .append(std::string(k, kb)) .append(">,<") .append(std::string(v, vb)) .append(">|"); return 0; }, &x); ASSERT_TRUE(x == "<1>,|<2>,|<è®°!>,|"); } TEST_F(VSMapTest, UsesGetAllAboveTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); std::string x; kv->get_above("B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "BB,5|BC,6|"); x = ""; kv->get_above("", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|"); x = ""; kv->get_above("ZZZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); x = ""; kv->get_above("B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "BB,5|BC,6|"); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); x = ""; kv->get_above( "B", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)) .append(",") .append(std::string(v, vb)) .append("|"); return 0; }, &x); ASSERT_TRUE(x == "BB,5|BC,6|è®°!,RR|"); } TEST_F(VSMapTest, UsesGetAllEqualAboveTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); std::string x; std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("B", cnt) == status::OK); ASSERT_EQ(3, cnt); kv->get_equal_above("B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "B,4|BB,5|BC,6|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("", cnt) == status::OK); ASSERT_EQ(6, cnt); x = ""; kv->get_equal_above("", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("ZZZ", cnt) == status::OK); ASSERT_EQ(0, cnt); x = ""; kv->get_equal_above("ZZZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("AZ", cnt) == status::OK); ASSERT_EQ(3, cnt); x = ""; kv->get_equal_above("AZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "B,4|BB,5|BC,6|"); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_above("B", cnt) == status::OK); ASSERT_EQ(4, cnt); x = ""; kv->get_equal_above( "B", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)) .append(",") .append(std::string(v, vb)) .append("|"); return 0; }, &x); ASSERT_TRUE(x == "B,4|BB,5|BC,6|è®°!,RR|"); } TEST_F(VSMapTest, UsesGetAllEqualBelowTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); std::string x; std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("B", cnt) == status::OK); ASSERT_EQ(4, cnt); kv->get_equal_below("B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("", cnt) == status::OK); ASSERT_EQ(0, cnt); x = ""; kv->get_equal_below("", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("ZZZ", cnt) == status::OK); ASSERT_EQ(6, cnt); x = ""; kv->get_equal_below("ZZZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|"); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("AZ", cnt) == status::OK); ASSERT_EQ(3, cnt); x = ""; kv->get_equal_below("AZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|"); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_equal_below("è®°!", cnt) == status::OK); ASSERT_EQ(7, cnt); x = ""; kv->get_equal_below( "è®°!", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)) .append(",") .append(std::string(v, vb)) .append("|"); return 0; }, &x); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|è®°!,RR|"); } TEST_F(VSMapTest, UsesGetAllBelowTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); std::string x; kv->get_below("AC", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|"); x = ""; kv->get_below("", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); x = ""; kv->get_below("ZZZZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|"); x = ""; kv->get_below("AC", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|"); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); x = ""; kv->get_below( "\xFF", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)) .append(",") .append(std::string(v, vb)) .append("|"); return 0; }, &x); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|è®°!,RR|"); } TEST_F(VSMapTest, UsesGetAllBetweenTest_TRACERS_M) { ASSERT_TRUE(kv->put("A", "1") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AB", "2") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("AC", "3") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("B", "4") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BB", "5") == status::OK) << errormsg(); ASSERT_TRUE(kv->put("BC", "6") == status::OK) << errormsg(); std::string x; kv->get_between("A", "B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "AB,2|AC,3|"); x = ""; kv->get_between("", "ZZZ", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|B,4|BB,5|BC,6|"); x = ""; kv->get_between("", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); x = ""; kv->get_between("", "B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "A,1|AB,2|AC,3|"); x = ""; kv->get_between("", "", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); kv->get_between("A", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); kv->get_between("AC", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); kv->get_between("B", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); kv->get_between("BD", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); kv->get_between("ZZZ", "A", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x.empty()); x = ""; kv->get_between("A", "B", [&](string_view k, string_view v) { x.append(k.data(), k.size()) .append(",") .append(v.data(), v.size()) .append("|"); return 0; }); ASSERT_TRUE(x == "AB,2|AC,3|"); ASSERT_TRUE(kv->put("è®°!", "RR") == status::OK) << errormsg(); x = ""; kv->get_between( "B", "\xFF", [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((std::string *)arg); c->append(std::string(k, kb)) .append(",") .append(std::string(v, vb)) .append("|"); return 0; }, &x); ASSERT_TRUE(x == "BB,5|BC,6|è®°!,RR|"); } // ============================================================================================= // TEST LARGE COLLECTIONS // ============================================================================================= const int LARGE_LIMIT = 4000000; TEST_F(VSMapLargeTest, LargeAscendingTest) { for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, (istr + "!")) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } for (int i = 1; i <= LARGE_LIMIT; i++) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == (istr + "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } TEST_F(VSMapLargeTest, LargeDescendingTest) { for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); ASSERT_TRUE(kv->put(istr, ("ABC" + istr)) == status::OK) << errormsg(); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } for (int i = LARGE_LIMIT; i >= 1; i--) { std::string istr = std::to_string(i); std::string value; ASSERT_TRUE(kv->get(istr, &value) == status::OK && value == ("ABC" + istr)); } std::size_t cnt = std::numeric_limits::max(); ASSERT_TRUE(kv->count_all(cnt) == status::OK); ASSERT_TRUE(cnt == LARGE_LIMIT); } pmemkv-1.1/tests/helgrind.supp000066400000000000000000000000001361504041500165060ustar00rootroot00000000000000pmemkv-1.1/tests/helpers.cmake000066400000000000000000000142251361504041500164630ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. set(DIR ${PARENT_DIR}/${TEST_NAME}) function(setup) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${BIN_DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BIN_DIR}) endfunction() function(cleanup) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${DIR}) endfunction() # Executes test command ${name} and verifies its status matches ${expectation}. # Optional function arguments are passed as consecutive arguments to # the command. function(execute_arg input expectation name) message(STATUS "Executing: ${name} ${ARGN}") if("${input}" STREQUAL "") execute_process(COMMAND ${name} ${ARGN} RESULT_VARIABLE RET OUTPUT_FILE ${BIN_DIR}/out ERROR_FILE ${BIN_DIR}/err) else() execute_process(COMMAND ${name} ${ARGN} RESULT_VARIABLE RET INPUT_FILE ${input} OUTPUT_FILE ${BIN_DIR}/out ERROR_FILE ${BIN_DIR}/err) endif() message(STATUS "Test ${name}:") file(READ ${BIN_DIR}/out OUT) message(STATUS "Stdout:\n${OUT}") file(READ ${BIN_DIR}/err ERR) message(STATUS "Stderr:\n${ERR}") if(NOT RET EQUAL expectation) message(FATAL_ERROR "${name} ${ARGN} exit code ${RET} doesn't match expectation: ${expectation}") endif() endfunction() # wrapper of execute_arg() function(execute name) execute_arg("" 0 ${name} ${ARGN}) endfunction() function(run_under_valgrind vg_opt name) message(STATUS "Executing: valgrind ${vg_opt} ${name} ${ARGN}") execute_process(COMMAND valgrind ${vg_opt} ${name} ${ARGN} RESULT_VARIABLE RET OUTPUT_FILE ${BIN_DIR}/out ERROR_FILE ${BIN_DIR}/err) message(STATUS "Test ${name}:") file(READ ${BIN_DIR}/out OUT) message(STATUS "Stdout:\n${OUT}") file(READ ${BIN_DIR}/err ERR) message(STATUS "Stderr:\n${ERR}") if(NOT RET EQUAL 0) message(FATAL_ERROR "command 'valgrind ${name} ${ARGN}' failed:\n${ERR}") endif() if(${TRACER} STREQUAL pmreorder) return(0) endif() set(text_passed "ERROR SUMMARY: 0 errors") string(FIND "${ERR}" "${text_passed}" RET) if(RET EQUAL -1) message(FATAL_ERROR "command 'valgrind ${vg_opt} ${name} ${ARGN}' failed:\n${ERR}") endif() endfunction() function(execute_tracer name) if (${TRACER} STREQUAL "none") execute(${name} ${ARGN}) elseif (${TRACER} STREQUAL memcheck) set(MEM_SUPP "${SRC_DIR}/memcheck.supp") set(VG_OPT "--leak-check=full" "--suppressions=${MEM_SUPP}") run_under_valgrind("${VG_OPT}" ${name} ${ARGN}) elseif (${TRACER} STREQUAL helgrind) set(HEL_SUPP "${SRC_DIR}/helgrind.supp") set(VG_OPT "--tool=helgrind" "--suppressions=${HEL_SUPP}") run_under_valgrind("${VG_OPT}" ${name} ${ARGN}) elseif (${TRACER} STREQUAL drd) set(DRD_SUPP "${SRC_DIR}/drd.supp") set(VG_OPT "--tool=drd" "--suppressions=${DRD_SUPP}") run_under_valgrind("${VG_OPT}" ${name} ${ARGN}) elseif (${TRACER} STREQUAL pmemcheck) set(VG_OPT "--tool=pmemcheck") run_under_valgrind("${VG_OPT}" ${name} ${ARGN}) elseif (${TRACER} STREQUAL pmreorder) set(ENV{PMREORDER_EMIT_LOG} 1) if(DEFINED ENV{PMREORDER_STACKTRACE_DEPTH}) set(PMREORDER_STACKTRACE_DEPTH $ENV{PMREORDER_STACKTRACE_DEPTH}) set(PMREORDER_STACKTRACE "yes") else() set(PMREORDER_STACKTRACE_DEPTH 1) set(PMREORDER_STACKTRACE "no") endif() set(VG_OPT "--tool=pmemcheck" "-q" "--log-stores=yes" "--print-summary=no" "--log-file=${BIN_DIR}/${TEST_NAME}.storelog" "--log-stores-stacktraces=${PMREORDER_STACKTRACE}" "--log-stores-stacktraces-depth=${PMREORDER_STACKTRACE_DEPTH}" "--expect-fence-after-clflush=yes") run_under_valgrind("${VG_OPT}" ${name} ${ARGN}) else () message(FATAL_ERROR "unknown tracer: ${TRACER}") endif () endfunction() # # pmreorder_create_store_log -- execute the command ${name} under pmemcheck and create a storelog # # Arguments: # name - path to the checker program with optional parameters # function(pmreorder_create_store_log name) if (NOT ${TRACER} STREQUAL pmreorder) message(FATAL_ERROR "pmreorder_create_store_log() can be called only with the 'pmreorder' tracer.") endif() execute_tracer(${name} ${ARGN}) endfunction() # # pmreorder_execute -- execute pmreorder # # Arguments: # engine - pmreorder engine type (for example 'ReorderAccumulative') # conf_file - path to the configuration file # name - path to the checker program with optional parameters # function(pmreorder_execute engine conf_file name) set(ENV{PMEMOBJ_CONF} "copy_on_write.at_open=1") set(cmd pmreorder -l ${BIN_DIR}/${TEST_NAME}.storelog -o ${BIN_DIR}/${TEST_NAME}.pmreorder -r ${engine} -x ${conf_file} -p "${name} ${ARGN}") execute(${cmd}) unset(ENV{PMEMOBJ_CONF}) endfunction() pmemkv-1.1/tests/memcheck.supp000066400000000000000000000007231361504041500165020ustar00rootroot00000000000000{ ndctl suppression Memcheck:Leak match-leak-kinds: definite fun:realloc ... fun:ndctl_pfn_get_first fun:ndctl_namespace_get_pfn ... } { ndctl suppression Memcheck:Leak match-leak-kinds: definite fun:realloc ... fun:ndctl_dax_get_first fun:ndctl_namespace_get_dax ... } { memory leak in ndctl 63 Memcheck:Leak match-leak-kinds: reachable fun:malloc ... fun:ndctl_namespace_get_first_badblock ... } pmemkv-1.1/tests/mock_tx_alloc.cc000066400000000000000000000045761361504041500171540ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include #include #include "mock_tx_alloc.h" thread_local bool tx_alloc_should_fail; extern "C" PMEMoid pmemobj_tx_alloc(size_t size, uint64_t type_num); PMEMoid pmemobj_tx_alloc(size_t size, uint64_t type_num) { static auto real = (decltype(pmemobj_tx_alloc) *)dlsym(RTLD_NEXT, "pmemobj_tx_alloc"); if (real == nullptr) abort(); if (tx_alloc_should_fail) { errno = ENOMEM; return OID_NULL; } return real(size, type_num); } PMEMoid pmemobj_tx_xalloc(size_t size, uint64_t type_num, uint64_t flags) { static auto real = (decltype(pmemobj_tx_xalloc) *)dlsym(RTLD_NEXT, "pmemobj_tx_xalloc"); if (real == nullptr) abort(); if (tx_alloc_should_fail) { errno = ENOMEM; return OID_NULL; } return real(size, type_num, flags); } pmemkv-1.1/tests/mock_tx_alloc.h000066400000000000000000000031771361504041500170120ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #pragma once extern thread_local bool tx_alloc_should_fail; pmemkv-1.1/tests/pmemkv_c_api_test.cc000066400000000000000000000153051361504041500200170ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../src/libpmemkv.h" #include "gtest/gtest.h" #include #include #include #include // Tests and params' list #include "basic_tests.h" class PmemkvCApiTest : public ::testing::TestWithParam { public: std::map init_status; pmemkv_db *db = NULL; Basic params = GetParam(); std::string path; void SetUp() { path = params.get_path(); std::remove(path.c_str()); if (!params.use_file) mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); pmemkv_config *cfg = pmemkv_config_new(); ASSERT_NE(cfg, nullptr) << pmemkv_errormsg(); ASSERT_EQ(pmemkv_config_put_string(cfg, "path", path.c_str()), PMEMKV_STATUS_OK) << pmemkv_errormsg(); ASSERT_EQ(pmemkv_config_put_uint64(cfg, "size", params.size), PMEMKV_STATUS_OK) << pmemkv_errormsg(); ASSERT_EQ(pmemkv_config_put_uint64(cfg, "force_create", params.force_create), PMEMKV_STATUS_OK) << pmemkv_errormsg(); ASSERT_EQ(pmemkv_open(params.engine, cfg, &db), PMEMKV_STATUS_OK) << pmemkv_errormsg(); } void TearDown() { pmemkv_close(db); std::remove(path.c_str()); } }; struct GetTestName { template std::string operator()(const testing::TestParamInfo &info) const { auto test = static_cast(info.param); auto name = test.name; if (!info.param.tracers.empty()) { name += "_TRACERS_" + info.param.tracers; } return name; } }; TEST_P(PmemkvCApiTest, PutAndGet) { /** * Test: Put data into db and get it back */ std::map proto_dictionary; /// Create test dictionary for (size_t i = 0; i < params.test_value_length; i++) { std::string key = std::to_string(i); key.insert(key.begin(), params.key_length - key.length(), '0'); std::string val = std::to_string(i); val.insert(val.begin(), params.value_length - val.length(), '0'); proto_dictionary[key] = val; } /// Put data into db for (const auto &record : proto_dictionary) { const char *key = record.first.c_str(); const char *val = record.second.c_str(); int s = pmemkv_put(db, key, strlen(key), val, strlen(val)); ASSERT_EQ(PMEMKV_STATUS_OK, s) << "Cannot put key: " << key << " with value: " << val; } /// Retrieve data from db and compare with prototype for (const auto &record : proto_dictionary) { char *buffer = new char[params.value_length + 1]; const char *key = record.first.c_str(); int s = pmemkv_get_copy(db, key, strlen(key), buffer, params.value_length + 1, NULL); ASSERT_EQ(PMEMKV_STATUS_OK, s) << "Cannot get value for key: " << key << ". " << pmemkv_errormsg(); ASSERT_STREQ(record.second.c_str(), buffer) << "Retrieved value is different than original"; delete[] buffer; } } /* Test if null can be passed as db to pmemkv_* functions */ TEST_P(PmemkvCApiTest, CheckNullDB) { size_t cnt; const char *key1 = "key1"; const char *value1 = "value1"; const char *key2 = "key2"; char val[10]; int s = pmemkv_count_all(NULL, &cnt); (void)s; ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_count_above(NULL, key1, strlen(key1), &cnt); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_count_below(NULL, key1, strlen(key1), &cnt); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_count_between(NULL, key1, strlen(key1), key2, strlen(key2), &cnt); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get_all(NULL, NULL, NULL); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get_above(NULL, key1, strlen(key1), NULL, NULL); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get_below(NULL, key1, strlen(key1), NULL, NULL); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get_between(NULL, key1, strlen(key1), key2, strlen(key2), NULL, NULL); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_exists(NULL, key1, strlen(key1)); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get(NULL, key1, strlen(key1), NULL, NULL); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_get_copy(NULL, key1, strlen(key1), val, 10, &cnt); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_put(NULL, key1, strlen(key1), value1, strlen(value1)); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_remove(NULL, key1, strlen(key1)); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); s = pmemkv_defrag(NULL, 0, 100); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); } TEST_P(PmemkvCApiTest, NullConfig) { /* XXX solve it generically, for all tests */ if (params.engine == std::string("blackhole")) return; pmemkv_config *empty_cfg = NULL; pmemkv_db *db = NULL; int s = pmemkv_open(params.engine, empty_cfg, &db); ASSERT_TRUE(s == PMEMKV_STATUS_INVALID_ARGUMENT) << pmemkv_errormsg(); } INSTANTIATE_TEST_CASE_P(basic_tests, PmemkvCApiTest, ::testing::ValuesIn(basic_tests), GetTestName()); pmemkv-1.1/tests/pmemkv_test.cc000066400000000000000000000054671361504041500166740ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "test_path.h" #include "gtest/gtest.h" #include #include #include #include #include std::string test_path; void print_option(std::string option, std::string description, std::string params = "") { std::cout << "\033[32m " << option << "\033[0m " << params << std::endl << " " << description << std::endl; } int main(int argc, char *argv[]) { std::vector args(argv + 1, argv + argc); auto help = std::find(args.begin(), args.end(), "--help"); if (help != args.end()) { print_option("--test_dir", "Path passed to engines config.", "PATH"); std::cout << std::endl; } auto test_dir_param = std::find(args.begin(), args.end(), "--test_dir"); if (test_dir_param != args.end()) { auto test_dir = std::next(test_dir_param, 1); if (test_dir != args.end()) { test_path.assign(*test_dir); } } bool gtest_list_tests = (std::find(args.begin(), args.end(), "--gtest_list_tests") != args.end()); if (!gtest_list_tests && test_path.empty()) { std::cerr << "Test path not specified" << std::endl; exit(1); } /* gtest is friendly creature, not at all evil, * so is ignoring non-gtest parameters */ ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } pmemkv-1.1/tests/pmreorder.conf000066400000000000000000000000501361504041500166540ustar00rootroot00000000000000{ "no_reorder" : "NoReorderNoCheck" } pmemkv-1.1/tests/run-binary.cmake000066400000000000000000000033351361504041500171070ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. include(${SRC_DIR}/helpers.cmake) setup() execute_process(COMMAND ${CMAKE_COMMAND} -E remove pool_${TEST_NAME}) execute(${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} pool_${TEST_NAME}) cleanup() pmemkv-1.1/tests/run-pmreorder-test.cmake000066400000000000000000000036751361504041500206060ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. include(${SRC_DIR}/helpers.cmake) setup() # create and initialize the pool execute(${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}) # create the store log while doing inserts pmreorder_create_store_log( ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}) # execute pmreorder: open the pool and check consistency pmreorder_execute(ReorderAccumulative ${SRC_DIR}/pmreorder.conf ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}) cleanup() pmemkv-1.1/tests/test_path.h000066400000000000000000000032561361504041500161650ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef TEST_PATH_H #define TEST_PATH_H #include extern std::string test_path; #endif // TEST_PATH_H pmemkv-1.1/tests/test_suite.h000066400000000000000000000050451361504041500163600ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #ifndef TEST_SUITE_H #define TEST_SUITE_H #include struct Basic { /* path parameter passed to engine config */ std::string *path; /* size parameter passed to engine config */ uint64_t size; /* force_create parameter passed to engine config */ uint64_t force_create; /* engine name */ const char *engine; /* key length */ size_t key_length; /* max size of data */ size_t value_length; /* size of actually inserted data */ size_t test_value_length; /* test name */ std::string name; /* markers for build system, which tracers should be used: * M - memcheck * P - pmemcheck * H - helgrind * D - drd */ std::string tracers; /* it specifies if engine should treat path as file or * directory */ bool use_file; std::string get_path() { std::string abs_path(*path); abs_path.append("/" + name); return abs_path; } }; std::ostream &operator<<(std::ostream &stream, const Basic &val) { stream << val.name; return stream; } #endif // TEST_SUITE_H pmemkv-1.1/tests/valgrind_wrapper.cc000066400000000000000000000065511361504041500176770ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include #include #include #include #include std::string get_filename(std::string path) { size_t pos = path.find_last_of("/\\"); return path.substr(pos + 1); } std::string get_cwd_rel_path(std::string path) { size_t pos = path.find_last_of("/\\"); return path.substr(0, pos); } int main(int argc, char *argv[]) { std::map> tool_options; tool_options["helgrind"] = std::make_pair("H", "--tool=helgrind"); tool_options["drd"] = std::make_pair("D", "--tool=drd"); tool_options["pmemcheck"] = std::make_pair("P", "--tool=pmemcheck"); tool_options["memcheck"] = std::make_pair("M", "--leak-check=full"); std::vector args(argv + 1, argv + argc); std::vector command; std::string app_name = get_filename(std::string(argv[0])); if (tool_options.find(app_name) == tool_options.end()) { std::cout << "Invalid application name. Use: "; for (auto &i : tool_options) std::cout << i.first << " "; std::cout << std::endl; exit(EXIT_FAILURE); } bool list_tests = (std::find(args.begin(), args.end(), "--gtest_list_tests") != args.end()); if (!list_tests) { command.push_back((char *)"valgrind"); command.push_back((char *)tool_options[app_name].second); } std::string bin_path = get_cwd_rel_path(std::string(argv[0])) + "/pmemkv_test"; command.push_back((char *)bin_path.c_str()); std::string memcheck_filter = "--gtest_filter=*TRACERS_*" + tool_options[app_name].first + "*"; if (list_tests) { std::cout << memcheck_filter.c_str(); command.push_back((char *)memcheck_filter.c_str()); } for (auto &i : args) { command.push_back((char *)i.c_str()); } command.push_back(NULL); execvp(command[0], command.data()); exit(EXIT_FAILURE); } pmemkv-1.1/tests/wrong_engine_name_test.cc000066400000000000000000000043611361504041500210460ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include "../src/libpmemkv.hpp" #include bool test_wrong_engine_name(std::string name) { pmem::kv::db db; return db.open(name) == pmem::kv::status::WRONG_ENGINE_NAME; } int main() { assert(test_wrong_engine_name("non_existent_name")); #ifndef ENGINE_CMAP assert(test_wrong_engine_name("cmap")); #endif #ifndef ENGINE_VSMAP assert(test_wrong_engine_name("vsmap")); #endif #ifndef ENGINE_VCMAP assert(test_wrong_engine_name("vcmap")); #endif #ifndef ENGINE_TREE3 assert(test_wrong_engine_name("tree3")); #endif #ifndef ENGINE_STREE assert(test_wrong_engine_name("stree")); #endif #ifndef ENGINE_CACHING assert(test_wrong_engine_name("caching")); #endif return 0; } pmemkv-1.1/travis.yml000066400000000000000000000015441361504041500147100ustar00rootroot00000000000000dist: trusty sudo: required language: cpp services: - docker env: global: - GITHUB_REPO=pmem/pmemkv - DOCKERHUB_REPO=pmem/pmemkv matrix: - TYPE=normal OS=fedora OS_VER=31 - TYPE=normal OS=ubuntu OS_VER=19.10 - TYPE=normal OS=ubuntu OS_VER=19.10 COVERAGE=1 - TYPE=building OS=fedora OS_VER=31 COVERAGE=1 PUSH_IMAGE=1 AUTO_DOC_UPDATE=1 - TYPE=building OS=ubuntu OS_VER=19.10 COVERAGE=1 PUSH_IMAGE=1 - TYPE=bindings OS=ubuntu OS_VER=19.10_bindings PUSH_IMAGE=1 - TYPE=compatibility OS=fedora OS_VER=31 - TYPE=coverity OS=ubuntu OS_VER=19.10 before_install: - echo $TRAVIS_COMMIT_RANGE - export HOST_WORKDIR=`pwd` - cd utils/docker - ./pull-or-rebuild-image.sh script: - ./build.sh after_success: - source ./set-vars.sh - if [[ -f $CI_FILE_PUSH_IMAGE_TO_REPO ]]; then images/push-image.sh $OS-$OS_VER; fi pmemkv-1.1/utils/000077500000000000000000000000001361504041500140115ustar00rootroot00000000000000pmemkv-1.1/utils/check_license/000077500000000000000000000000001361504041500165705ustar00rootroot00000000000000pmemkv-1.1/utils/check_license/check-headers.sh000077500000000000000000000143601361504041500216210ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # check-headers.sh - check copyright and license in source files SELF=$0 function usage() { echo "Usage: $SELF [-h|-v|-a]" echo " -h, --help this help message" echo " -v, --verbose verbose mode" echo " -a, --all check all files (only modified files are checked by default)" } if [ "$#" -lt 3 ]; then usage >&2 exit 2 fi SOURCE_ROOT=$1 shift CHECK_LICENSE=$1 shift LICENSE=$1 shift PATTERN=`mktemp` TMP=`mktemp` TMP2=`mktemp` TEMPFILE=`mktemp` rm -f $PATTERN $TMP $TMP2 function exit_if_not_exist() { if [ ! -f $1 ]; then echo "Error: file $1 does not exist. Exiting..." >&2 exit 1 fi } if [ "$1" == "-h" -o "$1" == "--help" ]; then usage exit 0 fi exit_if_not_exist $LICENSE exit_if_not_exist $CHECK_LICENSE export GIT="git -C ${SOURCE_ROOT}" $GIT rev-parse || exit 1 if [ -f $SOURCE_ROOT/.git/shallow ]; then SHALLOW_CLONE=1 echo echo "Warning: This is a shallow clone. Checking dates in copyright headers" echo " will be skipped in case of files that have no history." echo else SHALLOW_CLONE=0 fi VERBOSE=0 CHECK_ALL=0 while [ "$1" != "" ]; do case $1 in -v|--verbose) VERBOSE=1 ;; -a|--all) CHECK_ALL=1 ;; esac shift done if [ $CHECK_ALL -eq 0 ]; then CURRENT_COMMIT=$($GIT log --pretty=%H -1) MERGE_BASE=$($GIT merge-base HEAD origin/master 2>/dev/null) [ -z $MERGE_BASE ] && \ MERGE_BASE=$($GIT log --pretty="%cN:%H" | grep GitHub | head -n1 | cut -d: -f2) [ -z $MERGE_BASE -o "$CURRENT_COMMIT" = "$MERGE_BASE" ] && \ CHECK_ALL=1 fi if [ $CHECK_ALL -eq 1 ]; then echo "Checking copyright headers of all files..." GIT_COMMAND="ls-tree -r --name-only HEAD" else echo echo "Warning: will check copyright headers of modified files only," echo " in order to check all files issue the following command:" echo " $ $SELF -a" echo " (e.g.: $ $SELF $SOURCE_ROOT $CHECK_LICENSE $LICENSE -a)" echo echo "Checking copyright headers of modified files only..." GIT_COMMAND="diff --name-only $MERGE_BASE $CURRENT_COMMIT" fi FILES=$($GIT $GIT_COMMAND | ${SOURCE_ROOT}/utils/check_license/file-exceptions.sh | \ grep -E -e '*\.[ch]{1,2}$' -e '*\.[ch]pp$' -e '*\.sh$' \ -e '*\.py$' -e '*\.map$' -e 'Makefile*' -e 'TEST*' \ -e '/common.inc$' -e '/match$' -e '/check_whitespace$' \ -e 'LICENSE$' -e 'CMakeLists.txt$' -e '*\.cmake$' | \ xargs) # jemalloc.mk has to be checked always, because of the grep rules above FILES="$FILES src/jemalloc/jemalloc.mk" # create a license pattern file $CHECK_LICENSE create $LICENSE $PATTERN [ $? -ne 0 ] && exit 1 RV=0 for file in $FILES ; do # The src_path is a path which should be used in every command except git. # git is called with -C flag so filepaths should be relative to SOURCE_ROOT src_path="${SOURCE_ROOT}/$file" [ ! -f $src_path ] && continue # ensure that file is UTF-8 encoded ENCODING=`file -b --mime-encoding $src_path` iconv -f $ENCODING -t "UTF-8" $src_path > $TEMPFILE YEARS=`$CHECK_LICENSE check-pattern $PATTERN $TEMPFILE $src_path` if [ $? -ne 0 ]; then echo -n $YEARS RV=1 else HEADER_FIRST=`echo $YEARS | cut -d"-" -f1` HEADER_LAST=` echo $YEARS | cut -d"-" -f2` if [ $SHALLOW_CLONE -eq 0 ]; then $GIT log --no-merges --format="%ai %aE" -- $file | sort > $TMP else # mark the grafted commits (commits with no parents) $GIT log --no-merges --format="%ai %aE grafted-%p-commit" -- $file | sort > $TMP fi # skip checking dates for non-Intel commits [[ ! $(tail -n1 $TMP) =~ "@intel.com" ]] && continue # skip checking dates for new files [ $(cat $TMP | wc -l) -le 1 ] && continue # grep out the grafted commits (commits with no parents) # and skip checking dates for non-Intel commits grep -v -e "grafted--commit" $TMP | grep -e "@intel.com" > $TMP2 [ $(cat $TMP2 | wc -l) -eq 0 ] && continue FIRST=`head -n1 $TMP2` LAST=` tail -n1 $TMP2` COMMIT_FIRST=`echo $FIRST | cut -d"-" -f1` COMMIT_LAST=` echo $LAST | cut -d"-" -f1` if [ "$COMMIT_FIRST" != "" -a "$COMMIT_LAST" != "" ]; then if [ $HEADER_LAST -lt $COMMIT_LAST ]; then if [ $HEADER_FIRST -lt $COMMIT_FIRST ]; then COMMIT_FIRST=$HEADER_FIRST fi COMMIT_LAST=`date +%G` if [ $COMMIT_FIRST -eq $COMMIT_LAST ]; then NEW=$COMMIT_LAST else NEW=$COMMIT_FIRST-$COMMIT_LAST fi echo "$file:1: error: wrong copyright date: (is: $YEARS, should be: $NEW)" >&2 RV=1 fi else echo "error: unknown commit dates in file: $file" >&2 RV=1 fi fi done rm -f $TMP $TMP2 $TEMPFILE # check if error found if [ $RV -eq 0 ]; then echo "Copyright headers are OK." else echo "Error(s) in copyright headers found!" >&2 fi exit $RV pmemkv-1.1/utils/check_license/check-license.c000066400000000000000000000312661361504041500214410ustar00rootroot00000000000000/* * Copyright 2016-2019, Intel Corporation * Copyright (c) 2016, Microsoft Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ /* * check-license.c -- check the license in the file */ #include #include #include #include #include #include #include #include #include #include #include #include #define LICENSE_MAX_LEN 2048 #define COPYRIGHT "Copyright " #define COPYRIGHT_LEN 10 #define COPYRIGHT_SYMBOL "(c) " #define COPYRIGHT_SYMBOL_LEN 4 #define YEAR_MIN 1900 #define YEAR_MAX 9999 #define YEAR_INIT_MIN 9999 #define YEAR_INIT_MAX 0 #define YEAR_LEN 4 #define LICENSE_BEG "Redistribution and use" #define LICENSE_END "THE POSSIBILITY OF SUCH DAMAGE." #define DIFF_LEN 50 #define COMMENT_STR_LEN 5 #define STR_MODE_CREATE "create" #define STR_MODE_PATTERN "check-pattern" #define STR_MODE_LICENSE "check-license" #define ERROR(fmt, ...) fprintf(stderr, "error: " fmt "\n", __VA_ARGS__) #define ERROR2(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) /* * help_str -- string for the help message */ static const char * const help_str = "Usage: %s [filename]\n" "\n" "Modes:\n" " create \n" " - create a license pattern file \n" " from the license text file \n" "\n" " check-pattern \n" " - check if a license in \n" " matches the license pattern in ,\n" " if it does, copyright dates are printed out (see below)\n" "\n" " check-license \n" " - check if a license in \n" " matches the license text in ,\n" " if it does, copyright dates are printed out (see below)\n" "\n" "In case of 'check_pattern' and 'check_license' modes,\n" "if the license is correct, it prints out copyright dates\n" "in the following format: OLDEST_YEAR-NEWEST_YEAR\n" "\n" "Return value: returns 0 on success and -1 on error.\n" "\n"; /* * read_pattern -- read the pattern from the 'path_pattern' file to 'pattern' */ static int read_pattern(const char *path_pattern, char *pattern) { int file_pattern; ssize_t ret; if ((file_pattern = open(path_pattern, O_RDONLY)) == -1) { ERROR("open(): %s: %s", strerror(errno), path_pattern); return -1; } ret = read(file_pattern, pattern, LICENSE_MAX_LEN); close(file_pattern); if (ret == -1) { ERROR("read(): %s: %s", strerror(errno), path_pattern); return -1; } else if (ret != LICENSE_MAX_LEN) { ERROR("read(): incorrect format of the license pattern" " file (%s)", path_pattern); return -1; } return 0; } /* * write_pattern -- write 'pattern' to the 'path_pattern' file */ static int write_pattern(const char *path_pattern, char *pattern) { int file_pattern; ssize_t ret; if ((file_pattern = open(path_pattern, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { ERROR("open(): %s: %s", strerror(errno), path_pattern); return -1; } ret = write(file_pattern, pattern, LICENSE_MAX_LEN); close(file_pattern); if (ret < LICENSE_MAX_LEN) { ERROR("write(): %s: %s", strerror(errno), path_pattern); return -1; } return 0; } /* * strstr2 -- locate two substrings in the string */ static int strstr2(const char *str, const char *sub1, const char *sub2, char **pos1, char **pos2) { *pos1 = strstr(str, sub1); *pos2 = strstr(str, sub2); if (*pos1 == NULL || *pos2 == NULL) return -1; return 0; } /* * format_license -- remove comments and redundant whitespaces from the license */ static void format_license(char *license, size_t length) { char comment_str[COMMENT_STR_LEN]; char *comment = license; size_t comment_len; int was_space; size_t w, r; /* detect a comment string */ while (*comment != '\n') comment--; /* is there any comment? */ if (comment + 1 != license) { /* separate out a comment */ strncpy(comment_str, comment, COMMENT_STR_LEN - 1); comment_str[COMMENT_STR_LEN - 1] = 0; comment = comment_str + 1; while (isspace(*comment)) comment++; while (!isspace(*comment)) comment++; *comment = '\0'; comment_len = strlen(comment_str); /* replace comments with spaces */ if (comment_len > 2) { while ((comment = strstr(license, comment_str)) != NULL) for (w = 1; w < comment_len; w++) comment[w] = ' '; } else { while ((comment = strstr(license, comment_str)) != NULL) comment[1] = ' '; } } /* replace multiple spaces with one space */ was_space = 0; for (r = w = 0; r < length; r++) { if (!isspace(license[r])) { if (was_space) { license[w++] = ' '; was_space = 0; } if (w < r) license[w] = license[r]; w++; } else { if (!was_space) was_space = 1; } } license[w] = '\0'; } /* * analyze_license -- check correctness of the license */ static int analyze_license(const char *name_to_print, char *buffer, char **license) { char *_license; size_t _length; char *beg_str, *end_str; if (strstr2(buffer, LICENSE_BEG, LICENSE_END, &beg_str, &end_str)) { if (!beg_str) ERROR2("%s:1: error: incorrect license" " (license should start with the string '%s')", name_to_print, LICENSE_BEG); else ERROR2("%s:1: error: incorrect license" " (license should end with the string '%s')", name_to_print, LICENSE_END); return -1; } _license = beg_str; assert((uintptr_t)end_str > (uintptr_t)beg_str); _length = (size_t)(end_str - beg_str) + strlen(LICENSE_END); _license[_length] = '\0'; format_license(_license, _length); *license = _license; return 0; } /* * create_pattern -- create 'pattern' from the 'path_license' file */ static int create_pattern(const char *path_license, char *pattern) { char buffer[LICENSE_MAX_LEN]; char *license; ssize_t ret; int file_license; if ((file_license = open(path_license, O_RDONLY)) == -1) { ERROR("open(): %s: %s", strerror(errno), path_license); return -1; } memset(buffer, 0, sizeof(buffer)); ret = read(file_license, buffer, LICENSE_MAX_LEN); close(file_license); if (ret == -1) { ERROR("read(): %s: %s", strerror(errno), path_license); return -1; } if (analyze_license(path_license, buffer, &license) == -1) return -1; strncpy(pattern, license, LICENSE_MAX_LEN); return 0; } /* * print_diff -- print the first difference between 'license' and 'pattern' */ static void print_diff(char *license, char *pattern, size_t len) { size_t i = 0; while (i < len && license[i] == pattern[i]) i++; license[i + 1] = '\0'; pattern[i + 1] = '\0'; i = (i - DIFF_LEN > 0) ? (i - DIFF_LEN) : 0; while (i > 0 && license[i] != ' ') i--; fprintf(stderr, " The first difference is at the end of the line:\n"); fprintf(stderr, " * License: %s\n", license + i); fprintf(stderr, " * Pattern: %s\n", pattern + i); } /* * verify_license -- compare 'license' with 'pattern' and check correctness * of the copyright line */ static int verify_license(const char *path_to_check, char *pattern, const char *filename) { char buffer[LICENSE_MAX_LEN]; char *license, *copyright; int file_to_check; ssize_t ret; int year_first, year_last; int min_year_first = YEAR_INIT_MIN; int max_year_last = YEAR_INIT_MAX; char *err_str = NULL; const char *name_to_print = filename ? filename : path_to_check; if ((file_to_check = open(path_to_check, O_RDONLY)) == -1) { ERROR("open(): %s: %s", strerror(errno), path_to_check); return -1; } memset(buffer, 0, sizeof(buffer)); ret = read(file_to_check, buffer, LICENSE_MAX_LEN); close(file_to_check); if (ret == -1) { ERROR("read(): %s: %s", strerror(errno), name_to_print); return -1; } if (analyze_license(name_to_print, buffer, &license) == -1) return -1; /* check the copyright notice */ copyright = buffer; while ((copyright = strstr(copyright, COPYRIGHT)) != NULL) { copyright += COPYRIGHT_LEN; /* skip the copyright symbol '(c)' if any */ if (strncmp(copyright, COPYRIGHT_SYMBOL, COPYRIGHT_SYMBOL_LEN) == 0) copyright += COPYRIGHT_SYMBOL_LEN; /* look for the first year */ if (!isdigit(*copyright)) { err_str = "no digit just after the 'Copyright ' string"; break; } year_first = atoi(copyright); if (year_first < YEAR_MIN || year_first > YEAR_MAX) { err_str = "the first year is wrong"; break; } copyright += YEAR_LEN; if (year_first < min_year_first) min_year_first = year_first; if (year_first > max_year_last) max_year_last = year_first; /* check if there is the second year */ if (*copyright == ',') continue; else if (*copyright != '-') { err_str = "'-' or ',' expected after the first year"; break; } copyright++; /* look for the second year */ if (!isdigit(*copyright)) { err_str = "no digit after '-'"; break; } year_last = atoi(copyright); if (year_last < YEAR_MIN || year_last > YEAR_MAX) { err_str = "the second year is wrong"; break; } copyright += YEAR_LEN; if (year_last > max_year_last) max_year_last = year_last; if (*copyright != ',') { err_str = "',' expected after the second year"; break; } } if (!err_str && min_year_first == YEAR_INIT_MIN) err_str = "no 'Copyright ' string found"; if (err_str) /* found an error in the copyright notice */ ERROR2("%s:1: error: incorrect copyright notice: %s", name_to_print, err_str); /* now check the license */ if (memcmp(license, pattern, strlen(pattern)) != 0) { ERROR2("%s:1: error: incorrect license", name_to_print); print_diff(license, pattern, strlen(pattern)); return -1; } if (err_str) return -1; /* all checks passed */ if (min_year_first != max_year_last && max_year_last != YEAR_INIT_MAX) { printf("%i-%i\n", min_year_first, max_year_last); } else { printf("%i\n", min_year_first); } return 0; } /* * mode_create_pattern_file -- 'create' mode function */ static int mode_create_pattern_file(const char *path_license, const char *path_pattern) { char pattern[LICENSE_MAX_LEN]; if (create_pattern(path_license, pattern) == -1) return -1; return write_pattern(path_pattern, pattern); } /* * mode_check_pattern -- 'check_pattern' mode function */ static int mode_check_pattern(const char *path_license, const char *path_to_check) { char pattern[LICENSE_MAX_LEN]; if (create_pattern(path_license, pattern) == -1) return -1; return verify_license(path_to_check, pattern, NULL); } /* * mode_check_license -- 'check_license' mode function */ static int mode_check_license(const char *path_pattern, const char *path_to_check, const char *filename) { char pattern[LICENSE_MAX_LEN]; if (read_pattern(path_pattern, pattern) == -1) return -1; return verify_license(path_to_check, pattern, filename); } int main(int argc, char *argv[]) { if (strcmp(argv[1], STR_MODE_CREATE) == 0) { if (argc != 4) goto invalid_args; return mode_create_pattern_file(argv[2], argv[3]); } else if (strcmp(argv[1], STR_MODE_PATTERN) == 0) { if (argc != 5) goto invalid_args; return mode_check_license(argv[2], argv[3], argv[4]); } else if (strcmp(argv[1], STR_MODE_LICENSE) == 0) { if (argc != 4) goto invalid_args; return mode_check_pattern(argv[2], argv[3]); } else { ERROR("wrong mode: %s\n", argv[1]); } invalid_args: printf(help_str, argv[0]); return -1; } pmemkv-1.1/utils/check_license/file-exceptions.sh000077500000000000000000000032331361504041500222260ustar00rootroot00000000000000#!/bin/sh -e # # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # file-exceptions.sh - filter out files not checked for copyright and license grep -v -E -e 'src/valgrind' pmemkv-1.1/utils/check_whitespace000077500000000000000000000111021361504041500172230ustar00rootroot00000000000000#!/usr/bin/env perl # # Copyright 2015-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # check_whitespace -- scrub source tree for whitespace errors # use strict; use warnings; use File::Basename; use File::Find; use Encode; use v5.16; my $Me = $0; $Me =~ s,.*/,,; $SIG{HUP} = $SIG{INT} = $SIG{TERM} = $SIG{__DIE__} = sub { die @_ if $^S; my $errstr = shift; die "$Me: ERROR: $errstr"; }; my $Errcount = 0; # # err -- emit error, keep total error count # sub err { warn @_, "\n"; $Errcount++; } # # decode_file_as_string -- slurp an entire file into memory and decode # sub decode_file_as_string { my ($full, $file) = @_; my $fh; open($fh, '<', $full) or die "$full $!\n"; local $/; $_ = <$fh>; close $fh; # check known encodings or die my $decoded; my @encodings = ("UTF-8", "UTF-16", "UTF-16LE", "UTF-16BE"); foreach my $enc (@encodings) { eval { $decoded = decode( $enc, $_, Encode::FB_CROAK ) }; if (!$@) { $decoded =~ s/\R/\n/g; return $decoded; } } die "$Me: ERROR: Unknown file encoding"; } # # check_whitespace -- run the checks on the given file # sub check_whitespace { my ($full, $file) = @_; my $line = 0; my $eol; my $nf = 0; my $fstr = decode_file_as_string($full, $file); for (split /^/, $fstr) { $line++; $eol = /[\n]/s; if (/^\.nf$/) { err("$full:$line: ERROR: nested .nf") if $nf; $nf = 1; } elsif (/^\.fi$/) { $nf = 0; } elsif ($nf == 0) { chomp; err("$full:$line: ERROR: trailing whitespace") if /\s$/; err("$full:$line: ERROR: spaces before tabs") if / \t/; } } err("$full:$line: .nf without .fi") if $nf; err("$full:$line: noeol") unless $eol; } sub check_whitespace_with_exc { my ($full) = @_; $_ = $full; $_ = basename($full); return 0 unless /^(README.*|LICENSE.*|CMakeLists.txt|.gitignore|check_whitespace|.*\.([chp13s]|cc|sh|map|cpp|hpp|inc|md|cmake))$/; return 0 if -z; check_whitespace($full, $_); return 1; } my $verbose = 0; my $force = 0; my $recursive = 0; sub check { my ($file) = @_; my $r; if ($force) { $r = check_whitespace($file, basename($file)); } else { $r = check_whitespace_with_exc($file); } if ($verbose) { if ($r == 0) { printf("skipped $file\n"); } else { printf("checked $file\n"); } } } my @files = (); foreach my $arg (@ARGV) { if ($arg eq '-v') { $verbose = 1; next; } if ($arg eq '-f') { $force = 1; next; } if ($arg eq '-r') { $recursive = 1; next; } if ($arg eq '-g') { @files = `git ls-tree -r --name-only HEAD`; chomp(@files); next; } if ($arg eq '-h') { printf "Options: -g - check all files tracked by git -r dir - recursively check all files in specified directory -v verbose - print whether file was checked or not -f force - disable blacklist\n"; exit 1; } if ($recursive == 1) { find(sub { my $full = $File::Find::name; if (!$force && ($full eq './.git')) { $File::Find::prune = 1; return; } return unless -f; push @files, $full; }, $arg); $recursive = 0; next; } push @files, $arg; } if (!@files) { printf "Empty file list!\n"; } foreach (@files) { check($_); } exit $Errcount; pmemkv-1.1/utils/cppstyle000077500000000000000000000044701361504041500156070ustar00rootroot00000000000000#!/usr/bin/perl -w # # Copyright 2017, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. use strict; use Text::Diff; my $clangfmt = shift or die; my $mode = shift or die; sub check { my ($file) = @_; my $original; my $formatted; $formatted = `$clangfmt -style=file "$file"`; if ($mode eq 'check') { local $/=undef; open FILE, "$file" or die "Couldn't open file: $file"; binmode FILE; $original = ; close FILE; my $diff = diff \$original, \$formatted; if ($diff ne "") { print "Style check using $clangfmt for file $file failed\n"; print $diff; die "Style check using $clangfmt for file $file failed\n"; } } elsif ($mode eq 'format') { local $/=undef; open FILE, '>', "$file" or die "Couldn't open file: $file"; print FILE "$formatted"; close FILE; } else { die 'unknown mode'; } } foreach(@ARGV) { check($_) } pmemkv-1.1/utils/docker/000077500000000000000000000000001361504041500152605ustar00rootroot00000000000000pmemkv-1.1/utils/docker/0001-travis-fix-travisci_build_coverity_scan.sh.patch000066400000000000000000000016251361504041500273210ustar00rootroot00000000000000From b5179dc4822eaab192361da05aa95d98f523960f Mon Sep 17 00:00:00 2001 From: Lukasz Dorau Date: Mon, 7 May 2018 12:05:40 +0200 Subject: [PATCH] travis: fix travisci_build_coverity_scan.sh --- travisci_build_coverity_scan.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/travisci_build_coverity_scan.sh b/travisci_build_coverity_scan.sh index ad9d4afcf..562b08bcc 100644 --- a/travisci_build_coverity_scan.sh +++ b/travisci_build_coverity_scan.sh @@ -92,8 +92,8 @@ response=$(curl \ --form description="Travis CI build" \ $UPLOAD_URL) status_code=$(echo "$response" | sed -n '$p') -if [ "$status_code" != "201" ]; then +if [ "$status_code" != "200" ]; then TEXT=$(echo "$response" | sed '$d') - echo -e "\033[33;1mCoverity Scan upload failed: $TEXT.\033[0m" + echo -e "\033[33;1mCoverity Scan upload failed: $response.\033[0m" exit 1 fi -- 2.13.6 pmemkv-1.1/utils/docker/build.sh000077500000000000000000000114211361504041500167150ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2017-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # build.sh - runs a Docker container from a Docker image with environment # prepared for running pmemkv build and tests. # # # Notes: # - run this script from its location or set the variable 'HOST_WORKDIR' to # where the root of this project is on the host machine, # - set variables 'OS' and 'OS_VER' properly to a system you want to build this # repo on (for proper values take a look on the list of Dockerfiles at the # utils/docker/images directory), eg. OS=ubuntu, OS_VER=16.04. # set -e if [[ -z "$OS" || -z "$OS_VER" ]]; then echo "ERROR: The variables OS and OS_VER have to be set " \ "(eg. OS=fedora, OS_VER=30)." exit 1 fi if [[ -z "$HOST_WORKDIR" ]]; then HOST_WORKDIR=$(readlink -f ../..) fi if [[ "$TRAVIS_EVENT_TYPE" == "cron" || "$TRAVIS_BRANCH" == "coverity_scan" ]]; then if [[ "$TYPE" != "coverity" ]]; then echo "Skipping non-Coverity job for cron/Coverity build" exit 0 fi else if [[ "$TYPE" == "coverity" ]]; then echo "Skipping Coverity job for non cron/Coverity build" exit 0 fi fi imageName=${DOCKERHUB_REPO}:1.1-${OS}-${OS_VER} containerName=pmemkv-${OS}-${OS_VER} if [[ "$command" == "" ]]; then case $TYPE in normal) command="./run-build.sh"; ;; compatibility) command="./run-compatibility.sh"; ;; building) command="./run-test-building.sh"; ;; coverity) command="./run-coverity.sh"; ;; bindings) command="./run-bindings.sh"; ;; esac fi if [ "$COVERAGE" == "1" ]; then docker_opts="${docker_opts} `bash <(curl -s https://codecov.io/env)`"; ci_env=`bash <(curl -s https://codecov.io/env)` fi if [ -n "$DNS_SERVER" ]; then DNS_SETTING=" --dns=$DNS_SERVER "; fi # Only run doc update on pmem/pmemkv master branch if [[ "$TRAVIS_BRANCH" != "master" || "$TRAVIS_PULL_REQUEST" != "false" || "$TRAVIS_REPO_SLUG" != "${GITHUB_REPO}" ]]; then AUTO_DOC_UPDATE=0 fi WORKDIR=/pmemkv SCRIPTSDIR=$WORKDIR/utils/docker # check if we are running on a CI (Travis or GitHub Actions) [ -n "$GITHUB_ACTIONS" -o -n "$TRAVIS" ] && CI_RUN="YES" || CI_RUN="NO" # do not allocate a pseudo-TTY if we are running on GitHub Actions [ ! $GITHUB_ACTIONS ] && TTY='-t' || TTY='' echo Building ${OS}-${OS_VER} # Run a container with # - environment variables set (--env) # - host directory containing source mounted (-v) # - working directory set (-w) docker run --privileged=true --name=$containerName -i $TTY \ $DNS_SETTING \ ${docker_opts} \ $ci_env \ --env http_proxy=$http_proxy \ --env https_proxy=$https_proxy \ --env AUTO_DOC_UPDATE=$AUTO_DOC_UPDATE \ --env GITHUB_TOKEN=$GITHUB_TOKEN \ --env WORKDIR=$WORKDIR \ --env SCRIPTSDIR=$SCRIPTSDIR \ --env COVERAGE=$COVERAGE \ --env TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG \ --env TRAVIS_BRANCH=$TRAVIS_BRANCH \ --env TRAVIS_EVENT_TYPE=$TRAVIS_EVENT_TYPE \ --env COVERITY_SCAN_TOKEN=$COVERITY_SCAN_TOKEN \ --env COVERITY_SCAN_NOTIFICATION_EMAIL=$COVERITY_SCAN_NOTIFICATION_EMAIL \ --env TEST_BUILD=$TEST_BUILD \ --env DEFAULT_TEST_DIR=/dev/shm \ --env TEST_PACKAGES=${TEST_PACKAGES:-ON} \ --env BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} \ --env CHECK_CPP_STYLE=${CHECK_CPP_STYLE:-ON} \ --env CI_RUN=$CI_RUN \ --shm-size=4G \ -v $HOST_WORKDIR:$WORKDIR \ -v /etc/localtime:/etc/localtime \ -w $SCRIPTSDIR \ $imageName $command pmemkv-1.1/utils/docker/images/000077500000000000000000000000001361504041500165255ustar00rootroot00000000000000pmemkv-1.1/utils/docker/images/Dockerfile.archlinux-base-latest000066400000000000000000000063331361504041500247220ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of archlinux-based # environment prepared for running pmemkv tests. # # Pull base image FROM archlinux/base:latest MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS archlinux-base ENV OS_VER latest ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the pacman cache and install basic tools RUN pacman -Syu --noconfirm RUN pacman -S --noconfirm \ asciidoc \ autoconf \ automake \ bash-completion \ clang \ cmake \ doxygen \ file \ gcc \ gdb \ git \ graphviz \ gtest \ intel-tbb \ libunwind \ llvm \ make \ numactl \ pandoc \ perl-text-diff \ pkg-config \ rapidjson \ ruby \ sfml \ sudo \ wget \ which \ whois \ xmlto # Install libndctl COPY install-libndctl.sh install-libndctl.sh RUN ./install-libndctl.sh archlinux-base # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh # Install memkind COPY install-memkind.sh install-memkind.sh RUN ./install-memkind.sh # Add user ENV USER user ENV USERPASS p1a2s3s4 ENV PFILE ./password RUN useradd -m $USER RUN echo $USERPASS > $PFILE RUN echo $USERPASS >> $PFILE RUN passwd $USER < $PFILE RUN rm -f $PFILE RUN sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers RUN gpasswd wheel -a $USER USER $USER # Clean the cache of packages RUN rm -rf /var/cache/pacman/pkg/* pmemkv-1.1/utils/docker/images/Dockerfile.centos-8000066400000000000000000000061121361504041500221560ustar00rootroot00000000000000# # Copyright 2016-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of centos-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM centos:8 MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS centos ENV OS_VER 8 ENV PACKAGE_MANAGER rpm ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD RUN dnf update -y RUN dnf install -y epel-release RUN dnf install -y 'dnf-command(config-manager)' RUN dnf config-manager --set-enabled PowerTools # Install basic tools RUN dnf update -y \ && dnf install -y \ autoconf \ automake \ clang \ cmake \ daxctl-devel \ doxygen \ gcc \ gcc-c++ \ gdb \ git \ graphviz \ gtest-devel \ libtool \ make \ man \ ndctl-devel \ numactl-devel \ pandoc \ passwd \ perl-Text-Diff \ python36 \ rpm-build \ sudo \ tbb-devel \ unzip \ wget \ which \ && dnf clean all # Install glibc-debuginfo RUN dnf debuginfo-install -y glibc # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh centos # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh rpm # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh RPM # Install memkind COPY install-memkind.sh install-memkind.sh RUN ./install-memkind.sh centos # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER RUN echo "$USER:$USERPASS" | chpasswd RUN gpasswd wheel -a $USER USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.debian-testing000066400000000000000000000057261361504041500234250ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of debian-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM debian:testing MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS debian ENV OS_VER testing ENV PACKAGE_MANAGER deb ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the Apt cache and install basic tools RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ autoconf \ automake \ build-essential \ clang \ clang-format \ cmake \ curl \ debhelper \ devscripts \ doxygen \ fakeroot \ git \ graphviz \ libc6-dbg \ libdaxctl-dev \ libgtest-dev \ libndctl-dev \ libmemkind-dev \ libnode-dev \ libnuma-dev \ libtbb-dev \ libtext-diff-perl \ libtool \ libunwind8-dev \ numactl \ pandoc \ pkg-config \ rapidjson-dev \ ruby \ sudo \ wget \ whois \ && rm -rf /var/lib/apt/lists/* # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh dpkg # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh DEB # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.debian-unstable000066400000000000000000000057301361504041500235600ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of debian-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM debian:unstable MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS debian ENV OS_VER unstable ENV PACKAGE_MANAGER deb ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the Apt cache and install basic tools RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ autoconf \ automake \ build-essential \ clang \ clang-format \ cmake \ curl \ debhelper \ devscripts \ doxygen \ fakeroot \ git \ graphviz \ libc6-dbg \ libdaxctl-dev \ libgtest-dev \ libndctl-dev \ libmemkind-dev \ libnode-dev \ libnuma-dev \ libtbb-dev \ libtext-diff-perl \ libtool \ libunwind8-dev \ numactl \ pandoc \ pkg-config \ rapidjson-dev \ ruby \ sudo \ wget \ whois \ && rm -rf /var/lib/apt/lists/* # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh dpkg # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh DEB # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.fedora-31000066400000000000000000000055621361504041500222070ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM fedora:31 MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS fedora ENV OS_VER 31 ENV PACKAGE_MANAGER rpm ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Install basic tools RUN dnf update -y \ && dnf install -y \ autoconf \ automake \ clang \ cmake \ daxctl-devel \ doxygen \ gcc \ gcc-c++ \ gdb \ git \ graphviz \ gtest-devel \ hub \ libtool \ make \ man \ memkind-devel \ ndctl-devel \ numactl-devel \ pandoc \ passwd \ perl-Text-Diff \ rapidjson-devel \ rpm-build \ sudo \ tbb-devel \ unzip \ wget \ which \ && dnf clean all # Install glibc-debuginfo RUN dnf debuginfo-install -y glibc # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh rpm # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh RPM # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER RUN echo "$USER:$USERPASS" | chpasswd RUN gpasswd wheel -a $USER USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.fedora-rawhide000066400000000000000000000055741361504041500234120ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM fedora:rawhide MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS fedora ENV OS_VER rawhide ENV PACKAGE_MANAGER rpm ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Install basic tools RUN dnf update -y \ && dnf install -y \ autoconf \ automake \ clang \ cmake \ daxctl-devel \ doxygen \ gcc \ gcc-c++ \ gdb \ git \ graphviz \ gtest-devel \ hub \ libtool \ make \ man \ memkind-devel \ ndctl-devel \ numactl-devel \ pandoc \ passwd \ perl-Text-Diff \ rapidjson-devel \ rpm-build \ sudo \ tbb-devel \ unzip \ wget \ which \ && dnf clean all # Install glibc-debuginfo RUN dnf debuginfo-install -y glibc # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh rpm # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh RPM # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER RUN echo "$USER:$USERPASS" | chpasswd RUN gpasswd wheel -a $USER USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.opensuse-leap-latest000066400000000000000000000072761361504041500246040ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of opensuse-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM opensuse/leap:latest MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS opensuse-leap ENV OS_VER latest ENV PACKAGE_MANAGER rpm ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the OS RUN zypper dup -y # Update all packages RUN zypper update -y # Install basic tools RUN zypper install -y \ autoconf \ automake \ bash-completion \ clang \ cmake \ doxygen \ fdupes \ gcc \ gcc-c++ \ gdb \ git \ graphviz \ gtest \ hub \ keyutils-devel \ libtool \ make \ man \ libjson-c-devel \ libkmod-devel \ libndctl-devel \ libnuma-devel \ libudev-devel \ libuuid-devel \ pandoc \ perl-Text-Diff \ 'pkgconfig(bash-completion)' \ rapidjson-devel \ rpm-build \ sudo \ unzip \ wget \ which # Enable the debuginfo repo RUN zypper mr -e repo-debug RUN zypper install -y \ glibc-debuginfo # Install a newer version of TBB from the Education repository RUN zypper addrepo https://download.opensuse.org/repositories/Education/openSUSE_Leap_15.1/ education RUN zypper --gpg-auto-import-keys install -y \ tbb-devel RUN zypper clean all # Install libndctl COPY install-libndctl.sh install-libndctl.sh RUN ./install-libndctl.sh opensuse # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh opensuse # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh rpm # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh RPM # Install memkind COPY install-memkind.sh install-memkind.sh RUN ./install-memkind.sh opensuse # Add user ENV USER user ENV USERPASS p1a2s3s4 ENV PFILE ./password RUN useradd -m $USER RUN echo $USERPASS > $PFILE RUN echo $USERPASS >> $PFILE RUN passwd $USER < $PFILE RUN rm -f $PFILE RUN sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers RUN groupadd wheel RUN gpasswd wheel -a $USER USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.opensuse-tumbleweed-latest000066400000000000000000000067411361504041500260140ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of opensuse-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM opensuse/tumbleweed:latest MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS opensuse-tumbleweed ENV OS_VER latest ENV PACKAGE_MANAGER rpm ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the OS RUN zypper dup -y # Update all packages RUN zypper update -y # Enable the debuginfo repo RUN zypper mr -e repo-debug # Install basic tools RUN zypper install -y \ autoconf \ automake \ bash-completion \ clang \ cmake \ doxygen \ fdupes \ gcc \ gcc-c++ \ gdb \ glibc-debuginfo \ git \ graphviz \ gtest \ hub \ keyutils-devel \ libtool \ make \ man \ libjson-c-devel \ libkmod-devel \ libndctl-devel \ libnuma-devel \ libudev-devel \ libuuid-devel \ pandoc \ perl-Text-Diff \ 'pkgconfig(bash-completion)' \ rapidjson-devel \ rpm-build \ sudo \ tbb-devel \ unzip \ wget \ which RUN zypper clean all # Install libndctl COPY install-libndctl.sh install-libndctl.sh RUN ./install-libndctl.sh opensuse # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh opensuse # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh rpm # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh RPM # Install memkind COPY install-memkind.sh install-memkind.sh RUN ./install-memkind.sh opensuse # Add user ENV USER user ENV USERPASS p1a2s3s4 ENV PFILE ./password RUN useradd -m $USER RUN echo $USERPASS > $PFILE RUN echo $USERPASS >> $PFILE RUN passwd $USER < $PFILE RUN rm -f $PFILE RUN sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers RUN groupadd wheel RUN gpasswd wheel -a $USER USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.ubuntu-19.10000066400000000000000000000057221361504041500225140ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM ubuntu:19.10 MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 19.10 ENV PACKAGE_MANAGER deb ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the Apt cache and install basic tools RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ autoconf \ automake \ build-essential \ clang \ clang-format \ cmake \ curl \ debhelper \ devscripts \ doxygen \ fakeroot \ git \ graphviz \ libc6-dbg \ libdaxctl-dev \ libgtest-dev \ libndctl-dev \ libmemkind-dev \ libnode-dev \ libnuma-dev \ libtbb-dev \ libtext-diff-perl \ libtool \ libunwind8-dev \ numactl \ pandoc \ pkg-config \ rapidjson-dev \ ruby \ sudo \ wget \ whois \ && rm -rf /var/lib/apt/lists/* # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh dpkg # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh DEB # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.ubuntu-19.10_bindings000066400000000000000000000064241361504041500243710ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for executing build and tests # of pmemkv-* bindings repositories. # # Pull base image FROM ubuntu:19.10 MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 19.10_bindings ENV PACKAGE_MANAGER deb ENV NOTTY 1 ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64 ENV JAVA_TOOL_OPTIONS -Dfile.encoding=UTF-8 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the Apt cache and install basic tools RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ autoconf \ automake \ build-essential \ clang \ clang-format \ cmake \ curl \ debhelper \ devscripts \ doxygen \ fakeroot \ git \ graphviz \ libc6-dbg \ libdaxctl-dev \ libgtest-dev \ libndctl-dev \ libnode-dev \ libnuma-dev \ libmemkind-dev \ libtbb-dev \ libtext-diff-perl \ libtool \ libunwind8-dev \ maven \ npm \ numactl \ openjdk-11-jdk \ pandoc \ pkg-config \ python3-dev \ python3-distutils \ rapidjson-dev \ ruby-dev \ sudo \ wget \ whois \ && rm -rf /var/lib/apt/lists/* # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh dpkg # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh DEB # Bindings COPY install-bindings-dependencies.sh install-bindings-dependencies.sh RUN ./install-bindings-dependencies.sh # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.1/utils/docker/images/Dockerfile.ubuntu-rolling000066400000000000000000000057261361504041500235160ustar00rootroot00000000000000# # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM ubuntu:rolling MAINTAINER szymon.romik@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER rolling ENV PACKAGE_MANAGER deb ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Update the Apt cache and install basic tools RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ autoconf \ automake \ build-essential \ clang \ clang-format \ cmake \ curl \ debhelper \ devscripts \ doxygen \ fakeroot \ git \ graphviz \ libc6-dbg \ libdaxctl-dev \ libgtest-dev \ libndctl-dev \ libmemkind-dev \ libnode-dev \ libnuma-dev \ libtbb-dev \ libtext-diff-perl \ libtool \ libunwind8-dev \ numactl \ pandoc \ pkg-config \ rapidjson-dev \ ruby \ sudo \ wget \ whois \ && rm -rf /var/lib/apt/lists/* # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh dpkg # Install pmdk c++ bindings COPY install-libpmemobj-cpp.sh install-libpmemobj-cpp.sh RUN ./install-libpmemobj-cpp.sh DEB # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.1/utils/docker/images/README.md000066400000000000000000000017131361504041500200060ustar00rootroot00000000000000# Content Dockerfiles and scripts placed in this directory are indended to be used as developement process vehicles and part of continuous integration process. Images built out of those recipes may by used with docker or podman as developement environment. Only those used on travis are fully tested on a daily basis. In case of any problem, patches and github issues are welcome. # How to build docker image ```sh docker build --build-arg https_proxy=https://proxy.com:port --build-arg http_proxy=http://proxy.com:port -t pmemkv:debian-unstable -f ./Dockerfile.debian-unstable . ``` # How to use docker image To run build and tests on local machine on docker: ```sh docker run --network=bridge --shm-size=4G -v /your/workspace/path/:/opt/workspace:z -w /opt/workspace/ -e CC=clang -e CXX=clang++ -e PKG_CONFIG_PATH=/opt/pmdk/lib/pkgconfig -it pmemkv:debian-unstable /bin/bash ``` To get strace working, add to docker commandline ```sh --cap-add SYS_PTRACE ``` pmemkv-1.1/utils/docker/images/build-image.sh000077500000000000000000000050031361504041500212410ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # build-image.sh - prepares a Docker image with -based # environment for testing pmemkv, according # to the Dockerfile. file located # in the same directory. # # The script can be run locally. # set -e function usage { echo "Usage:" echo " build-image.sh " echo "where , for example, can be 'fedora-30', provided " \ "a Dockerfile named 'Dockerfile.fedora-30' exists in the " \ "current directory." } # Check if the first and second argument is nonempty if [[ -z "$1" || -z "$2" ]]; then usage exit 1 fi # Check if the file Dockerfile.OS-VER exists if [[ ! -f "Dockerfile.$2" ]]; then echo "ERROR: wrong argument." usage exit 1 fi # Build a Docker image tagged with ${DOCKERHUB_REPO}:1.1-OS-VER docker build -t $1:1.1-$2 \ --build-arg http_proxy=$http_proxy \ --build-arg https_proxy=$https_proxy \ -f Dockerfile.$2 . pmemkv-1.1/utils/docker/images/install-bindings-dependencies.sh000077500000000000000000000113441361504041500247540ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-bindings-dependencies.sh - installs dependencies for bindings # so they can be built offline. This step is optional while # setting up local environment. # set -e # master: Merge pull request #531 from lukaszstolarczuk/add-skip-build-flags-to-dockerimages, 20.11.2019 PMEMKV_VERSION="6ad5c453f79cb2ddd1ee9ba2a9dff294c6cb7b71" # master: Merge pull request #44 from lukaszstolarczuk/update-travis-files, 21.11.2019 RUBY_VERSION="3741e3df698245fc8a15822a1aa85b5c211fd332" # master: Merge pull request #33 from lukaszstolarczuk/update-travis-files, 21.11.2019 JNI_VERSION="5239d6bb3214c56bc45b3296872be50b38bfbab3" # master: Merge pull request #34 from lukaszstolarczuk/update-offline-de..., 5.12.2019 JAVA_VERSION="47f02b6b52c56ca53fd3dafdff52167719f1e7dd" # master: Merge pull request #48 from lukaszstolarczuk/update-travis-files, 21.11.2019 NODEJS_VERSION="d19b026207e8a78ebffdccaffb27181a9bdbe51d" PREFIX=/usr rm -rf /opt/bindings WORKDIR=$(pwd) # # 1) Build and install PMEMKV - Java will need it # git clone https://github.com/pmem/pmemkv.git cd pmemkv git checkout $PMEMKV_VERSION mkdir build cd build # only VSMAP engine is enabled, because Java tests need it cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DENGINE_VSMAP=ON \ -DENGINE_CMAP=OFF \ -DENGINE_VCMAP=OFF \ -DENGINE_CACHING=OFF \ -DENGINE_STREE=OFF \ -DBUILD_EXAMPLES=OFF \ -DENGINE_TREE3=OFF make -j$(nproc) make -j$(nproc) install # # 2) RUBY dependencies - all of the dependencies (gems) needed to run # pmemkv-ruby will be saved # in the /opt/bindings/ruby directory cd $WORKDIR mkdir -p /opt/bindings/ruby/ gem install bundler -v '< 2.0' git clone https://github.com/pmem/pmemkv-ruby.git cd pmemkv-ruby git checkout $RUBY_VERSION # bundle package command copies all of the .gem files needed to run # the application into the vendor/cache directory bundle package mv -v vendor/cache/* /opt/bindings/ruby/ # # 3) Build and install JNI # cd $WORKDIR git clone https://github.com/pmem/pmemkv-jni.git cd pmemkv-jni git checkout $JNI_VERSION make test make install prefix=$PREFIX # # 4) JAVA dependencies - all of the dependencies needed to run # pmemkv-java will be saved # in the /opt/bindings/java directory cd $WORKDIR mkdir -p /opt/bindings/java/ git clone https://github.com/pmem/pmemkv-java.git cd pmemkv-java git checkout $JAVA_VERSION mvn dependency:go-offline mvn install mv -v ~/.m2/repository /opt/bindings/java/ # # 5) NodeJS dependencies - all of the dependencies needed to run # pmemkv-nodejs will be saved # in the /opt/bindings/nodejs/ directory cd $WORKDIR mkdir -p /opt/bindings/nodejs/ git clone https://github.com/pmem/pmemkv-nodejs.git cd pmemkv-nodejs git checkout $NODEJS_VERSION npm install --save cp -rv ./node_modules /opt/bindings/nodejs/ # # Uninstall all installed stuff # cd $WORKDIR/pmemkv/build make uninstall cd $WORKDIR/pmemkv-jni make uninstall cd $WORKDIR/pmemkv-nodejs npm uninstall cd $WORKDIR rm -r pmemkv pmemkv-ruby pmemkv-jni pmemkv-java pmemkv-nodejs # make the /opt/bindings directory world-readable chmod -R a+r /opt/bindings pmemkv-1.1/utils/docker/images/install-libndctl.sh000077500000000000000000000053701361504041500223300ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-libndctl.sh - installs libndctl # set -e OS=$1 echo "==== clone ndctl repo ====" git clone https://github.com/pmem/ndctl.git cd ndctl git checkout tags/v64.1 if [ "$OS" = "fedora" ]; then echo "==== setup rpmbuild tree ====" rpmdev-setuptree RPMDIR=$HOME/rpmbuild/ VERSION=$(./git-version) SPEC=./rhel/ndctl.spec echo "==== create source tarball =====" git archive --format=tar --prefix="ndctl-${VERSION}/" HEAD | gzip > "$RPMDIR/SOURCES/ndctl-${VERSION}.tar.gz" echo "==== build ndctl ====" ./autogen.sh ./configure --disable-docs make -j$(nproc) echo "==== build ndctl packages ====" rpmbuild -ba $SPEC echo "==== install ndctl packages ====" rpm -i $RPMDIR/RPMS/x86_64/*.rpm echo "==== cleanup ====" rm -rf $RPMDIR else echo "==== set OS-specific options ====" OS_SPECIFIC="" LIBDIR=/usr/lib case $(echo $OS | cut -d'-' -f1) in centos|opensuse) LIBDIR=/usr/lib64 ;; archlinux) OS_SPECIFIC="--disable-dependency-tracking" ;; esac echo "==== build ndctl ====" ./autogen.sh ./configure --libdir=$LIBDIR --disable-docs $OS_SPECIFIC make -j$(nproc) echo "==== install ndctl ====" make -j$(nproc) install echo "==== cleanup ====" fi cd .. rm -rf ndctl pmemkv-1.1/utils/docker/images/install-libpmemobj-cpp.sh000077500000000000000000000045501361504041500234340ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-libpmemobj-cpp.sh # - installs PMDK C++ bindings (libpmemobj-cpp) # set -e if [ "${SKIP_LIBPMEMOBJCPP_BUILD}" ]; then echo "Variable 'SKIP_LIBPMEMOBJCPP_BUILD' is set; skipping building libpmemobj-cpp" exit fi PREFIX=/usr PACKAGE_TYPE=$1 # v1.9; 31.01.2020 LIBPMEMOBJ_CPP_VERSION="1.9" git clone https://github.com/pmem/libpmemobj-cpp --shallow-since=2019-10-02 cd libpmemobj-cpp git checkout $LIBPMEMOBJ_CPP_VERSION mkdir build cd build cmake .. -DCPACK_GENERATOR="$PACKAGE_TYPE" -DCMAKE_INSTALL_PREFIX=$PREFIX if [ "$PACKAGE_TYPE" = "" ]; then make -j$(nproc) install else make -j$(nproc) package if [ "$PACKAGE_TYPE" = "DEB" ]; then sudo dpkg -i libpmemobj++*.deb elif [ "$PACKAGE_TYPE" = "RPM" ]; then sudo rpm -i libpmemobj++*.rpm fi fi cd ../.. rm -r libpmemobj-cpp pmemkv-1.1/utils/docker/images/install-memkind.sh000077500000000000000000000042151361504041500221560ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-memkind.sh - installs memkind from sources; depends on # the system it uses proper installation paramaters # set -e OS=$1 # v1.10.0, contains new libmemkind namespace MEMKIND_VERSION=v1.10.0 WORKDIR=$(pwd) git clone https://github.com/memkind/memkind cd $WORKDIR/memkind git checkout $MEMKIND_VERSION # set OS-specific configure options OS_SPECIFIC="" case $(echo $OS | cut -d'-' -f1) in centos|opensuse) OS_SPECIFIC="--libdir=/usr/lib64" ;; esac ./autogen.sh ./configure --prefix=/usr $OS_SPECIFIC make -j$(nproc) make -j$(nproc) install # cleanup cd $WORKDIR rm -r memkind pmemkv-1.1/utils/docker/images/install-pmdk.sh000077500000000000000000000047661361504041500215000ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-pmdk.sh - installs PMDK # set -e if [ "${SKIP_PMDK_BUILD}" ]; then echo "Variable 'SKIP_PMDK_BUILD' is set; skipping building PMDK" exit fi PACKAGE_TYPE=$1 PREFIX=${2:-/usr} # v1.8, 31.01.2020 PMDK_VERSION="1.8" git clone https://github.com/pmem/pmdk cd pmdk git checkout $PMDK_VERSION if [ "$PACKAGE_TYPE" = "" ]; then make -j$(nproc) install prefix=$PREFIX else make -j$(nproc) BUILD_PACKAGE_CHECK=n $PACKAGE_TYPE if [ "$PACKAGE_TYPE" = "dpkg" ]; then sudo dpkg -i dpkg/libpmem_*.deb dpkg/libpmem-dev_*.deb sudo dpkg -i dpkg/libpmemobj_*.deb dpkg/libpmemobj-dev_*.deb sudo dpkg -i dpkg/pmreorder_*.deb sudo dpkg -i dpkg/libpmemblk_*.deb dpkg/libpmemlog_*.deb dpkg/libpmempool_*.deb dpkg/pmempool_*.deb elif [ "$PACKAGE_TYPE" = "rpm" ]; then sudo rpm -i rpm/*/pmdk-debuginfo-*.rpm sudo rpm -i rpm/*/libpmem*-*.rpm sudo rpm -i rpm/*/pmreorder-*.rpm sudo rpm -i rpm/*/pmempool-*.rpm fi fi cd .. rm -r pmdk pmemkv-1.1/utils/docker/images/install-valgrind.sh000077500000000000000000000042601361504041500223400ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # install-valgrind.sh - installs valgrind for persistent memory # set -e if [ "${SKIP_VALGRIND_BUILD}" ]; then echo "Variable 'SKIP_VALGRIND_BUILD' is set; skipping building valgrind (pmem's fork)" exit fi OS=$1 git clone https://github.com/pmem/valgrind.git cd valgrind # valgrind v3.15 with pmemcheck git checkout c27a8a2f973414934e63f1e94bc84c0a580e3840 # set OS-specific configure options OS_SPECIFIC="" case $(echo $OS | cut -d'-' -f1) in centos|opensuse) OS_SPECIFIC="--libdir=/usr/lib64" ;; esac ./autogen.sh ./configure --prefix=/usr $OS_SPECIFIC make -j$(nproc) sudo make -j$(nproc) install cd .. rm -r valgrind pmemkv-1.1/utils/docker/images/push-image.sh000077500000000000000000000050561361504041500211310ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # push-image.sh - pushes the Docker image tagged with OS-VER # to the Docker Hub. # # The script utilizes $DOCKERHUB_USER and $DOCKERHUB_PASSWORD variables to log in to # Docker Hub. The variables can be set in the Travis project's configuration # for automated builds. # set -e function usage { echo "Usage:" echo " push-image.sh " echo "where , for example, can be 'fedora-30', provided " \ "a Docker image tagged with ${DOCKERHUB_REPO}:1.1-fedora-30 exists " \ "locally." } # Check if the first argument is nonempty if [[ -z "$1" ]]; then usage exit 1 fi # Check if the image tagged with ${DOCKERHUB_REPO}:1.1-OS-VER exists locally if [[ ! $(docker images -a | awk -v pattern="^${DOCKERHUB_REPO}:1.1-$1\$" \ '$1":"$2 ~ pattern') ]] then echo "ERROR: wrong argument." usage exit 1 fi # Log in to the Docker Hub docker login -u="$DOCKERHUB_USER" -p="$DOCKERHUB_PASSWORD" # Push the image to the repository docker push ${DOCKERHUB_REPO}:1.1-$1 pmemkv-1.1/utils/docker/prepare-for-build.sh000077500000000000000000000034671361504041500211500ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # prepare-for-build.sh - prepare the Docker image for the build # set -e function sudo_password() { echo $USERPASS | sudo -Sk $* } # this should be run only on CIs if [ "$CI_RUN" == "YES" ]; then sudo_password chown -R $(id -u).$(id -g) $WORKDIR fi || true pmemkv-1.1/utils/docker/pull-or-rebuild-image.sh000077500000000000000000000144241361504041500217220ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # pull-or-rebuild-image.sh - rebuilds the Docker image used in the # current Travis build if necessary. # # The script rebuilds the Docker image if the Dockerfile for the current # OS version (Dockerfile.${OS}-${OS_VER}) or any .sh script from the directory # with Dockerfiles were modified and committed. # # If the Travis build is not of the "pull_request" type (i.e. in case of # merge after pull_request) and it succeed, the Docker image should be pushed # to the Docker Hub repository. An empty file is created to signal that to # further scripts. # # If the Docker image does not have to be rebuilt, it will be pulled from # Docker Hub. # set -e source $(dirname $0)/set-vars.sh function get_commit_range_from_last_merge { # get commit id of the last merge LAST_MERGE=$(git log --merges --pretty=%H -1) LAST_COMMIT=$(git log --pretty=%H -1) if [ "$LAST_MERGE" == "$LAST_COMMIT" ]; then # GitHub Actions commits its own merge in case of pull requests # so the first merge commit has to be skipped. LAST_MERGE=$(git log --merges --pretty=%H -2 | tail -n1) fi if [ "$LAST_MERGE" == "" ]; then # possible in case of shallow clones COMMIT_RANGE="" else COMMIT_RANGE="$LAST_MERGE..HEAD" # make sure it works now if ! git rev-list $COMMIT_RANGE >/dev/null; then COMMIT_RANGE="" fi fi echo $COMMIT_RANGE } if [[ "$TRAVIS_EVENT_TYPE" != "cron" && "$TRAVIS_BRANCH" != "coverity_scan" \ && "$TYPE" == "coverity" ]]; then echo "INFO: Skip Coverity scan job if build is triggered neither by " \ "'cron' nor by a push to 'coverity_scan' branch" exit 0 fi if [[ ( "$TRAVIS_EVENT_TYPE" == "cron" || "$TRAVIS_BRANCH" == "coverity_scan" )\ && "$TYPE" != "coverity" ]]; then echo "INFO: Skip regular jobs if build is triggered either by 'cron'" \ " or by a push to 'coverity_scan' branch" exit 0 fi if [[ -z "$OS" || -z "$OS_VER" ]]; then echo "ERROR: The variables OS and OS_VER have to be set properly " \ "(eg. OS=ubuntu, OS_VER=16.04)." exit 1 fi if [[ -z "$HOST_WORKDIR" ]]; then echo "ERROR: The variable HOST_WORKDIR has to contain a path to " \ "the root of this project on the host machine" exit 1 fi # TRAVIS_COMMIT_RANGE is usually invalid for force pushes - fix it when used # with non-upstream repository if [ -n "$TRAVIS_COMMIT_RANGE" -a "$TRAVIS_REPO_SLUG" != "$GITHUB_REPO" ]; then if ! git rev-list $TRAVIS_COMMIT_RANGE; then TRAVIS_COMMIT_RANGE=$(get_commit_range_from_last_merge) fi fi # Fix Travis commit range if [ -n "$TRAVIS_COMMIT_RANGE" ]; then # $TRAVIS_COMMIT_RANGE contains "..." instead of ".." # https://github.com/travis-ci/travis-ci/issues/4596 PR_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE/.../..}" fi # Set the commit range in case of GitHub Actions if [ -n "$GITHUB_ACTIONS" ]; then PR_COMMIT_RANGE=$(get_commit_range_from_last_merge) fi # Find all the commits for the current build if [ -n "$PR_COMMIT_RANGE" ]; then commits=$(git rev-list $PR_COMMIT_RANGE) elif [ -n "$TRAVIS" ]; then commits=$TRAVIS_COMMIT elif [ -n "$GITHUB_ACTIONS" ]; then commits=$GITHUB_SHA else commits=$(git log --pretty=%H -1) fi echo "Commits in the commit range:" for commit in $commits; do echo $commit; done # Get the list of files modified by the commits files=$(for commit in $commits; do git diff-tree --no-commit-id --name-only \ -r $commit; done | sort -u) echo "Files modified within the commit range:" for file in $files; do echo $file; done # Path to directory with Dockerfiles and image building scripts images_dir_name=images base_dir=utils/docker/$images_dir_name # Check if committed file modifications require the Docker image to be rebuilt for file in $files; do # Check if modified files are relevant to the current build if [[ $file =~ ^($base_dir)\/Dockerfile\.($OS)-($OS_VER)$ ]] \ || [[ $file =~ ^($base_dir)\/.*\.sh$ ]] then # Rebuild Docker image for the current OS version echo "Rebuilding the Docker image for the Dockerfile.$OS-$OS_VER" pushd $images_dir_name ./build-image.sh ${DOCKERHUB_REPO} ${OS}-${OS_VER} popd # Check if the image has to be pushed to Docker Hub # (i.e. the build is triggered by commits to the ${GITHUB_REPO} # repository's master branch, and the Travis build is not # of the "pull_request" type). In that case, create the empty # file. if [[ "${TRAVIS_REPO_SLUG}" == "${GITHUB_REPO}" \ && ($TRAVIS_BRANCH == stable-* || $TRAVIS_BRANCH == master) \ && $TRAVIS_EVENT_TYPE != "pull_request" && $PUSH_IMAGE == "1" ]] then echo "The image will be pushed to Docker Hub" touch $CI_FILE_PUSH_IMAGE_TO_REPO else echo "Skip pushing the image to Docker Hub" fi exit 0 fi done # Getting here means rebuilding the Docker image is not required. # Pull the image from Docker Hub. docker pull ${DOCKERHUB_REPO}:1.1-${OS}-${OS_VER} pmemkv-1.1/utils/docker/run-bindings.sh000077500000000000000000000112541361504041500202210ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # run-bindings.sh - checks bindings' building and installation # set -e PREFIX=/usr # master: Merge pull request #44 from lukaszstolarczuk/update-travis-files, 21.11.2019 RUBY_VERSION="3741e3df698245fc8a15822a1aa85b5c211fd332" # master: Merge pull request #33 from lukaszstolarczuk/update-travis-files, 21.11.2019 JNI_VERSION="5239d6bb3214c56bc45b3296872be50b38bfbab3" # master: Merge pull request #34 from lukaszstolarczuk/update-offline-de..., 5.12.2019 JAVA_VERSION="47f02b6b52c56ca53fd3dafdff52167719f1e7dd" # master: Merge pull request #48 from lukaszstolarczuk/update-travis-files, 21.11.2019 NODEJS_VERSION="d19b026207e8a78ebffdccaffb27181a9bdbe51d" # master: Merge pull request #16 from lukaszstolarczuk/update-travis-files, 21.11.2019 PYTHON_VERSION="4483f6561a94255546d26f0e9ac4cdcfe209feae" ./prepare-for-build.sh function sudo_password() { echo $USERPASS | sudo -Sk $* } # build and install pmemkv cd $WORKDIR mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=$PREFIX make -j$(nproc) sudo_password -S make -j$(nproc) install echo echo "##################################################################" echo "### Verifying building and installing of the pmemkv-ruby bindings " echo "##################################################################" cd ~ git clone https://github.com/pmem/pmemkv-ruby.git cd pmemkv-ruby git checkout $RUBY_VERSION mkdir -p vendor/cache/ cp -r /opt/bindings/ruby/* vendor/cache/ bundle install --local bundle exec rspec echo echo "#################################################################" echo "### Verifying building and installing of the pmemkv-jni bindings " echo "#################################################################" cd ~ git clone https://github.com/pmem/pmemkv-jni.git cd pmemkv-jni git checkout $JNI_VERSION make test sudo_password -S make install prefix=$PREFIX echo echo "##################################################################" echo "### Verifying building and installing of the pmemkv-java bindings " echo "##################################################################" cd ~ git clone https://github.com/pmem/pmemkv-java.git cd pmemkv-java git checkout $JAVA_VERSION mkdir -p ~/.m2/repository cp -r /opt/bindings/java/repository ~/.m2/ mvn --offline install echo echo "####################################################################" echo "### Verifying building and installing of the pmemkv-nodejs bindings " echo "####################################################################" cd ~ git clone https://github.com/pmem/pmemkv-nodejs.git cd pmemkv-nodejs git checkout $NODEJS_VERSION cp -r /opt/bindings/nodejs/node_modules . npm install --save npm test echo echo "####################################################################" echo "### Verifying building and installing of the pmemkv-python bindings " echo "####################################################################" cd ~ git clone https://github.com/pmem/pmemkv-python.git cd pmemkv-python git checkout $PYTHON_VERSION python3 setup.py install --user cd tests python3 -m unittest -v pmemkv_tests.py cd ../examples PMEM_IS_PMEM_FORCE=1 python3 basic_example.py pmemkv-1.1/utils/docker/run-build.sh000077500000000000000000000111461361504041500175230ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # run-build.sh - is called inside a Docker container, # starts pmemkv build with tests. # set -e ./prepare-for-build.sh EXAMPLE_TEST_DIR="/tmp/build_example" PREFIX=/usr TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} CHECK_CPP_STYLE=${CHECK_CPP_STYLE:-ON} function sudo_password() { echo $USERPASS | sudo -Sk $* } function cleanup() { find . -name ".coverage" -exec rm {} \; find . -name "coverage.xml" -exec rm {} \; find . -name "*.gcov" -exec rm {} \; find . -name "*.gcda" -exec rm {} \; } function upload_codecov() { clang_used=$(cmake -LA -N . | grep CMAKE_CXX_COMPILER | grep clang | wc -c) if [[ $clang_used > 0 ]]; then gcovexe="llvm-cov gcov" else gcovexe="gcov" fi # the output is redundant in this case, i.e. we rely on parsed report from codecov on github bash <(curl -s https://codecov.io/bash) -c -F $1 -x "$gcovexe" cleanup } function compile_example_standalone() { rm -rf $EXAMPLE_TEST_DIR mkdir $EXAMPLE_TEST_DIR cd $EXAMPLE_TEST_DIR cmake $WORKDIR/examples/$1 # exit on error if [[ $? != 0 ]]; then cd - return 1 fi make -j$(nproc) cd - } function run_example_standalone() { cd $EXAMPLE_TEST_DIR ./$1 $2 # exit on error if [[ $? != 0 ]]; then cd - return 1 fi rm -f $2 cd - } cd $WORKDIR echo echo "### Making sure there is no libpmemkv currently installed" echo "---------------------------- Error expected! ------------------------------" compile_example_standalone pmemkv_basic_cpp && exit 1 echo "---------------------------------------------------------------------------" echo echo "##############################################################" echo "### Verify build and install (in dir: ${PREFIX})" echo "##############################################################" mkdir $WORKDIR/build cd $WORKDIR/build cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=$TEST_DIR \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DCOVERAGE=$COVERAGE \ -DENGINE_STREE=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DCHECK_CPP_STYLE=${CHECK_CPP_STYLE} \ -DDEVELOPER_MODE=1 make -j$(nproc) make -j$(nproc) doc ctest --output-on-failure sudo_password -S make -j$(nproc) install if [ "$COVERAGE" == "1" ]; then upload_codecov tests fi # Verify installed libraries compile_example_standalone pmemkv_basic_c run_example_standalone pmemkv_basic_c pool compile_example_standalone pmemkv_basic_cpp run_example_standalone pmemkv_basic_cpp pool if [ "$BUILD_JSON_CONFIG" == "ON" ]; then compile_example_standalone pmemkv_config_c run_example_standalone pmemkv_config_c pool fi compile_example_standalone pmemkv_pmemobj_cpp run_example_standalone pmemkv_pmemobj_cpp pool # Poolset example compile_example_standalone pmemkv_open_cpp pmempool create -l "pmemkv" obj $WORKDIR/examples/example.poolset run_example_standalone pmemkv_open_cpp $WORKDIR/examples/example.poolset # Expect failure - non-existsing path is passed run_example_standalone pmemkv_open_cpp /non-existing/path && exit 1 # Uninstall libraries cd $WORKDIR/build sudo_password -S make uninstall cd $WORKDIR rm -rf $WORKDIR/build pmemkv-1.1/utils/docker/run-compatibility.sh000077500000000000000000000060111361504041500212700ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # run-compatibility.sh - verify compatibility # set -e TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} PREFIX_HEAD=/opt/pmemkv-head PREFIX_1_0_1=/opt/pmemkv-1.0.1 function sudo_password() { echo $USERPASS | sudo -Sk $* } ./prepare-for-build.sh # build and install pmemkv head mkdir $WORKDIR/build cd $WORKDIR/build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=$PREFIX_HEAD make -j$(nproc) sudo_password -S make -j$(nproc) install cd $WORKDIR rm -rf $WORKDIR/build # build and install pmemkv 1.0.1 mkdir $WORKDIR/build cd $WORKDIR/build git clone https://github.com/pmem/pmemkv pmemkv-1.0.1 cd pmemkv-1.0.1 git checkout 1.0.1 mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=$PREFIX_1_0_1 make -j$(nproc) sudo_password -S make -j$(nproc) install cd $WORKDIR rm -rf $WORKDIR/build echo echo "##################################################################" echo "### Verifying compatibility with 1.0.1" echo "##################################################################" mkdir $WORKDIR/build cd $WORKDIR/build mkdir head cd head PKG_CONFIG_PATH=$PREFIX_HEAD/lib64/pkgconfig cmake ../../tests/compatibility make -j$(nproc) cd .. mkdir 1.0.1 cd 1.0.1 PKG_CONFIG_PATH=$PREFIX_1_0_1/lib64/pkgconfig cmake ../../tests/compatibility make -j$(nproc) cd .. PMEM_IS_PMEM_FORCE=1 $WORKDIR/tests/compatibility/cmap.sh $WORKDIR/build/head/cmap_compatibility $WORKDIR/build/1.0.1/cmap_compatibility $TEST_DIR/testfile pmemkv-1.1/utils/docker/run-coverity.sh000077500000000000000000000052101361504041500202630ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # run-coverity.sh - runs the Coverity scan build # set -e ./prepare-for-build.sh cd $WORKDIR mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Debug export COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG" [[ "$TRAVIS_EVENT_TYPE" == "cron" ]] \ && export COVERITY_SCAN_BRANCH_PATTERN="master" \ || export COVERITY_SCAN_BRANCH_PATTERN="coverity_scan" export COVERITY_SCAN_BUILD_COMMAND="make" # Run the Coverity scan # XXX: Patch the Coverity script. # Recently, this script regularly exits with an error, even though # the build is successfully submitted. Probably because the status code # is missing in response, or it's not 201. # Changes: # 1) change the expected status code to 200 and # 2) print the full response string. # # This change should be reverted when the Coverity script is fixed. # # The previous version was: # curl -s https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh | bash wget https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh patch < ../utils/docker/0001-travis-fix-travisci_build_coverity_scan.sh.patch bash ./travisci_build_coverity_scan.sh pmemkv-1.1/utils/docker/run-doc-update.sh000077500000000000000000000061261361504041500204530ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. set -e ORIGIN="https://${GITHUB_TOKEN}@github.com/pmem-bot/pmemkv" UPSTREAM="https://github.com/pmem/pmemkv" CURR_DIR=$(pwd) # Clone repo git clone ${ORIGIN} cd $CURR_DIR/pmemkv git remote add upstream ${UPSTREAM} git config --local user.name "pmem-bot" git config --local user.email "pmem-bot@intel.com" git checkout master git remote update git rebase upstream/master # Build docs mkdir $CURR_DIR/pmemkv/build cd $CURR_DIR/pmemkv/build cmake .. -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF make -j$(nproc) doc cp $CURR_DIR/pmemkv/build/man/tmp/*.md $CURR_DIR/pmemkv/doc/ cp -r $CURR_DIR/pmemkv/doc $CURR_DIR/ cp -r $CURR_DIR/pmemkv/build/doc/cpp_html $CURR_DIR/ cd $CURR_DIR/pmemkv # Checkout gh-pages and copy docs git checkout -fb gh-pages upstream/gh-pages git clean -df cp -f $CURR_DIR/doc/*.md master/manpages/ cp -fr $CURR_DIR/cpp_html/* master/doxygen/ # Fix the title tag: # get rid of _MP macro, it changes e.g. "_MP(PMEMKV, 7)" to "PMEMKV" sed -i 's/^title:\ _MP(*\([A-Za-z_-]*\).*$/title:\ \1/g' master/manpages/*.md # Add and push changes. # git commit command may fail if there is nothing to commit. # In that case we want to force push anyway (there might be open pull request with # changes which were reverted). git add -A git commit -m "doc: automatic gh-pages docs update" && true git push -f ${ORIGIN} gh-pages # Makes pull request. # When there is already an open PR or there are no changes an error is thrown, which we ignore. hub pull-request -f -b pmem:gh-pages -h pmem-bot:gh-pages -m "doc: automatic gh-pages docs update" && true exit 0 pmemkv-1.1/utils/docker/run-test-building.sh000077500000000000000000000204551361504041500212010ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # run-test-building.sh - is called inside a Docker container, # starts testing of pmemkv building # and automatic update of the documentation # set -e ./prepare-for-build.sh EXAMPLE_TEST_DIR="/tmp/build_example" PREFIX=/usr TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} TEST_PACKAGES=${TEST_PACKAGES:-ON} BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} function sudo_password() { echo $USERPASS | sudo -Sk $* } function cleanup() { find . -name ".coverage" -exec rm {} \; find . -name "coverage.xml" -exec rm {} \; find . -name "*.gcov" -exec rm {} \; find . -name "*.gcda" -exec rm {} \; } function upload_codecov() { clang_used=$(cmake -LA -N . | grep CMAKE_CXX_COMPILER | grep clang | wc -c) if [[ $clang_used > 0 ]]; then gcovexe="llvm-cov gcov" else gcovexe="gcov" fi # the output is redundant in this case, i.e. we rely on parsed report from codecov on github bash <(curl -s https://codecov.io/bash) -c -F $1 -x "$gcovexe" cleanup } function compile_example_standalone() { rm -rf $EXAMPLE_TEST_DIR mkdir $EXAMPLE_TEST_DIR cd $EXAMPLE_TEST_DIR cmake $WORKDIR/examples/$1 # exit on error if [[ $? != 0 ]]; then cd - return 1 fi make -j$(nproc) cd - } function run_example_standalone() { cd $EXAMPLE_TEST_DIR rm -f pool ./$1 pool # exit on error if [[ $? != 0 ]]; then cd - return 1 fi cd - } function run_test_check_support_cpp20_gcc() { CWD=$(pwd) echo echo "##############################################################" echo "### Checking C++20 support in g++" echo "##############################################################" mkdir $WORKDIR/build cd $WORKDIR/build CXX=g++ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DTEST_DIR=$TEST_DIR \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DCOVERAGE=$COVERAGE \ -DDEVELOPER_MODE=1 \ -DCXX_STANDARD=20 make -j$(nproc) # Run basic tests ctest -R "SimpleTest" --output-on-failure cd $CWD rm -rf $WORKDIR/build } function run_test_check_support_cpp20_clang() { CWD=$(pwd) echo echo "##############################################################" echo "### Checking C++20 support in clang++" echo "##############################################################" mkdir $WORKDIR/build cd $WORKDIR/build CXX=clang++ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DTEST_DIR=$TEST_DIR \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DCOVERAGE=$COVERAGE \ -DDEVELOPER_MODE=1 \ -DCXX_STANDARD=20 make -j$(nproc) # Run basic tests ctest -R "SimpleTest" --output-on-failure cd $CWD rm -rf $WORKDIR/build } function verify_building_of_packages() { echo echo "##############################################################" echo "### Verifying building of packages" echo "##############################################################" # Fetch git history for `git describe` to work, # so that package has proper 'version' field [ -f .git/shallow ] && git fetch --unshallow --tags mkdir $WORKDIR/build cd $WORKDIR/build cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=$TEST_DIR \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DDEVELOPER_MODE=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DCPACK_GENERATOR=$PACKAGE_MANAGER echo echo "### Making sure there is no libpmemkv currently installed" echo "---------------------------- Error expected! ------------------------------" compile_example_standalone pmemkv_basic_cpp && exit 1 echo "---------------------------------------------------------------------------" make -j$(nproc) package if [ $PACKAGE_MANAGER = "deb" ]; then sudo_password dpkg -i libpmemkv*.deb elif [ $PACKAGE_MANAGER = "rpm" ]; then sudo_password rpm -i libpmemkv*.rpm fi # Verify installed packages compile_example_standalone pmemkv_basic_c run_example_standalone pmemkv_basic_c compile_example_standalone pmemkv_basic_cpp run_example_standalone pmemkv_basic_cpp # Clean after installation if [ $PACKAGE_MANAGER = "deb" ]; then sudo_password dpkg -r libpmemkv-dev elif [ $PACKAGE_MANAGER = "rpm" ]; then sudo_password rpm -e --nodeps libpmemkv-devel fi cd $WORKDIR rm -rf $WORKDIR/build } cd $WORKDIR CMAKE_VERSION=$(cmake --version | head -n1 | grep -oE '[0-9].[0-9]*') CMAKE_VERSION_MAJOR=$(echo $CMAKE_VERSION | cut -d. -f1) CMAKE_VERSION_MINOR=$(echo $CMAKE_VERSION | cut -d. -f2) CMAKE_VERSION_NUMBER=$((100 * $CMAKE_VERSION_MAJOR + $CMAKE_VERSION_MINOR)) # CXX_STANDARD==20 is supported since CMake 3.12 if [ $CMAKE_VERSION_NUMBER -ge 312 ]; then run_test_check_support_cpp20_gcc run_test_check_support_cpp20_clang fi echo echo "##############################################################" echo "### Verifying if each engine is building properly" echo "##############################################################" engines_flags=( ENGINE_VSMAP ENGINE_VCMAP ENGINE_CMAP # XXX: caching engine requires libacl and memcached installed in docker images # and firstly we need to remove hardcoded INCLUDE paths (see #244) # ENGINE_CACHING ENGINE_STREE ENGINE_TREE3 # the last item is to test all engines disabled BLACKHOLE_TEST ) for engine_flag in "${engines_flags[@]}" do mkdir $WORKDIR/build cd $WORKDIR/build # testing each engine separately; disabling default engines echo echo "##############################################################" echo "### Verifying building of the '$engine_flag' engine" echo "##############################################################" cmake .. -DENGINE_VSMAP=OFF \ -DENGINE_VCMAP=OFF \ -DENGINE_CMAP=OFF \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -D$engine_flag=ON make -j$(nproc) # list all tests in this build ctest -N ctest -R wrong_engine_name_test --output-on-failure if [ "$COVERAGE" == "1" ]; then upload_codecov tests fi cd $WORKDIR rm -rf $WORKDIR/build done echo echo "##############################################################" echo "### Verifying building of all engines" echo "##############################################################" mkdir $WORKDIR/build cd $WORKDIR/build cmake .. -DENGINE_VSMAP=ON \ -DENGINE_VCMAP=ON \ -DENGINE_CMAP=ON \ -DENGINE_STREE=ON \ -DENGINE_TREE3=ON \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} make -j$(nproc) # list all tests in this build ctest -N cd $WORKDIR rm -rf $WORKDIR/build # building of packages should be verified only if PACKAGE_MANAGER equals 'rpm' or 'deb' case $PACKAGE_MANAGER in rpm|deb) [ "$TEST_PACKAGES" == "ON" ] && verify_building_of_packages ;; *) echo "Notice: skipping building of packages because PACKAGE_MANAGER is not equal 'rpm' nor 'deb' ..." ;; esac # Trigger auto doc update on master if [[ "$AUTO_DOC_UPDATE" == "1" ]]; then echo "Running auto doc update" mkdir -p $WORKDIR/doc_update cd $WORKDIR/doc_update $SCRIPTSDIR/run-doc-update.sh cd $WORKDIR rm -rf $WORKDIR/doc_update fi pmemkv-1.1/utils/docker/set-vars.sh000077500000000000000000000032561361504041500173710ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # set-vars.sh - set required environment variables # set -e export CI_FILE_PUSH_IMAGE_TO_REPO=/tmp/push_image_to_repo_flag pmemkv-1.1/utils/md2man/000077500000000000000000000000001361504041500151675ustar00rootroot00000000000000pmemkv-1.1/utils/md2man/default.man000066400000000000000000000042171361504041500173140ustar00rootroot00000000000000$if(has-tables)$ .\"t $endif$ $if(pandoc-version)$ .\" Automatically generated by Pandoc $pandoc-version$ .\" $endif$ $if(adjusting)$ .ad $adjusting$ $endif$ .TH "$title$" "$section$" "$date$" "PMEMKV - $secondary_title$ version $version$" "PMEMKV Programmer's Manual" $if(hyphenate)$ .hy $else$ .nh \" Turn off hyphenation by default. $endif$ $for(header-includes)$ $header-includes$ $endfor$ .\" Copyright 2019-$year$, Intel Corporation .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" .\" * Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" .\" * Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" * Neither the name of the copyright holder nor the names of its .\" contributors may be used to endorse or promote products derived .\" from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "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 COPYRIGHT .\" OWNER OR CONTRIBUTORS 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. $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ $if(author)$ .SH AUTHORS $for(author)$$author$$sep$; $endfor$. $endif$ pmemkv-1.1/utils/md2man/md2man.sh000077500000000000000000000052731361504041500167130ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2016-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "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 COPYRIGHT # OWNER OR CONTRIBUTORS 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. # # # md2man.sh -- convert markdown to groff man pages # # usage: md2man.sh file template outfile # # This script converts markdown file into groff man page using pandoc. # It performs some pre- and post-processing for better results: # - parse input file for YAML metadata block and read man page title, # section and version # - cut-off metadata block and license # - unindent code blocks # set -e set -o pipefail filename=$1 template=$2 outfile=$3 version=$4 title=`sed -n 's/^title:\ _MP(*\([A-Za-z_-]*\).*$/\1/p' $filename` section=`sed -n 's/^title:.*\([0-9]\))$/\1/p' $filename` secondary_title=`sed -n 's/^secondary_title:\ *\(.*\)$/\1/p' $filename` dt="$(date --utc --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%F)" # since genereted docs are not kept in the repo the output dir may not exist out_dir=`echo $outfile | sed 's/\(.*\)\/.*/\1/'` mkdir -p $out_dir cat $filename | sed -n -e '/# NAME #/,$p' |\ pandoc -s -t man -o $outfile --template=$template \ -V title=$title -V section=$section \ -V date="$dt" -V version="$version" \ -V year=$(date +"%Y") -V secondary_title="$secondary_title" | sed '/^\.IP/{ N /\n\.nf/{ s/IP/PP/ } }'