pax_global_header00006660000000000000000000000064141000042330014475gustar00rootroot0000000000000052 comment=a92abed550ece9c5c70b6be17db8e9cb19e328e4 pmemkv-1.5.0/000077500000000000000000000000001410000423300127775ustar00rootroot00000000000000pmemkv-1.5.0/.clang-format000066400000000000000000000016561410000423300153620ustar00rootroot00000000000000AccessModifierOffset: -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.5.0/.gitattributes000066400000000000000000000000721410000423300156710ustar00rootroot00000000000000* text=auto eol=lf *.jpg binary *.png binary *.gif binary pmemkv-1.5.0/.github/000077500000000000000000000000001410000423300143375ustar00rootroot00000000000000pmemkv-1.5.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001410000423300165225ustar00rootroot00000000000000pmemkv-1.5.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000035331410000423300212200ustar00rootroot00000000000000--- name: Bug report about: Did you find a bug in pmemkv? Please let us know. labels: "Type: Bug" --- # ISSUE: ## Environment Information | Name | Version | | :--------------------------- | :-------------------------: | | pmemkv version(s) | | | libpmemobj-cpp version(s) | | | PMDK (libpmem/libpmemobj) version(s) | | | OS(es) version(s) | | | kernel version(s) | | | compiler, libraries, packaging and other related tools version(s) | | and if applicable: | Name | Version | | :-------------------------- | :---------: | | TBB version(s): | | | memkind version(s): | | | ndctl version(s): | | ## Please provide a reproduction of the bug: ## How often bug is revealed: (always, often, rare) ## Actual behavior: ## Expected behavior: ## Details ## Additional information about Priority and Help Requested: | Question | Answer | | :------- | :----: | | Are you willing to submit a pull request with a proposed change? | Yes/No | | Requested priority | Showstopper/High/Medium/Low | pmemkv-1.5.0/.github/ISSUE_TEMPLATE/feature.md000066400000000000000000000005101410000423300204730ustar00rootroot00000000000000--- name: Feature about: Request a feature labels: "Type: Feature" --- # FEAT: ## Rationale ## Description ## API Changes ## Implementation details ## Meta pmemkv-1.5.0/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000006131410000423300207130ustar00rootroot00000000000000--- name: Question about: Do you have question regarding pmemkv? Don't hesitate to ask. labels: "Type: Question" --- # QUESTION: ## Details pmemkv-1.5.0/.github/workflows/000077500000000000000000000000001410000423300163745ustar00rootroot00000000000000pmemkv-1.5.0/.github/workflows/coverity.yml000066400000000000000000000020531410000423300207630ustar00rootroot00000000000000 name: pmemkv-coverity # It runs static analysis build - Coverity. It requires special token (set in CI's secret). on: schedule: # run this job at 00:00 UTC everyday - cron: '0 0 * * *' env: REPO: pmemkv GITHUB_REPO: pmem/pmemkv CONTAINER_REG: ghcr.io/pmem/pmemkv HOST_WORKDIR: ${{ github.workspace }} WORKDIR: utils/docker IMG_VER: latest COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }} COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} jobs: linux: name: Linux-coverity runs-on: ubuntu-latest strategy: matrix: CONFIG: ["TYPE=coverity OS=ubuntu OS_VER=20.04"] steps: - name: Clone the git repo uses: actions/checkout@v2 # coverity workflow should just reuse (pull) the most recent, available image - name: Pull the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh pull - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh pmemkv-1.5.0/.github/workflows/gha.yml000066400000000000000000000073001410000423300176560ustar00rootroot00000000000000 name: pmemkv # It runs default OSes for each PR, push event or a new tag, # checks basic builds with various compilers and executes all sets of tests. on: push: pull_request: release: types: - created env: REPO: pmemkv GITHUB_REPO: pmem/pmemkv CONTAINER_REG: ghcr.io/pmem/pmemkv HOST_WORKDIR: ${{ github.workspace }} WORKDIR: utils/docker TEST_TIMEOUT: 600 IMG_VER: latest jobs: linux: name: pmemkv runs-on: ubuntu-latest env: # use org's Private Access Token to log in to GitHub Container Registry CONTAINER_REG_USER: ${{ secrets.GH_CR_USER }} CONTAINER_REG_PASS: ${{ secrets.GH_CR_PAT }} FORCE_IMAGE_ACTION: ${{ secrets.FORCE_IMAGE_ACTION }} strategy: matrix: CONFIG: ["TYPE=debug OS=fedora OS_VER=32", "TYPE=debug OS=ubuntu OS_VER=20.10", "TYPE=debug OS=ubuntu OS_VER=20.04 CHECK_CPP_STYLE=1 COVERAGE=1", "TYPE=release OS=fedora OS_VER=32", "TYPE=release OS=ubuntu OS_VER=20.04 PUSH_IMAGE=1", "TYPE=valgrind OS=ubuntu OS_VER=20.04", "TYPE=memcheck_drd OS=ubuntu OS_VER=20.04", "TYPE=building OS=fedora OS_VER=32 PUSH_IMAGE=1", "TYPE=building OS=ubuntu OS_VER=20.10 PUSH_IMAGE=1 COVERAGE=1 ", "TYPE=compatibility OS=fedora OS_VER=32", "TYPE=bindings OS=ubuntu OS_VER=20.04_bindings PUSH_IMAGE=1"] steps: - name: Set image version and force image action for stable branch # we want to set IMG_VER to e.g. '1.x' for stable branches and PRs against them # for PRs we have to use 'base_ref' - this is the target branch (and we have to check that instead) if: startsWith(github.ref, 'refs/heads/stable-') || startsWith(github.base_ref, 'stable-') # we now know we're on (or against) stable branches, so we just need to pick the version (e.g. the mentioned '1.x') run: | IMG_VER=$(echo ${GITHUB_BASE_REF} | cut -d - -f 2) [ -z "${IMG_VER}" ] \ && echo "IMG_VER=$(echo ${GITHUB_REF#refs/heads/} | cut -d - -f 2)" >> $GITHUB_ENV \ || echo "IMG_VER=${IMG_VER}" >> $GITHUB_ENV echo "FORCE_IMAGE_ACTION=rebuild" >> $GITHUB_ENV - name: Clone the git repo uses: actions/checkout@v2 with: fetch-depth: 0 # "pull" or "rebuild" can be passed to a secret FORCE_IMAGE_ACTION to override default action - name: Pull the image or rebuild and push it run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh $FORCE_IMAGE_ACTION - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh doc: name: build and publish docs runs-on: ubuntu-latest needs: linux env: DOC_UPDATE_GITHUB_TOKEN: ${{ secrets.DOC_UPDATE_GITHUB_TOKEN }} DOC_UPDATE_BOT_NAME: ${{ secrets.DOC_UPDATE_BOT_NAME }} DOC_REPO_OWNER: ${{ secrets.DOC_REPO_OWNER }} if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/stable-') strategy: matrix: CONFIG: ["TYPE=doc OS=fedora OS_VER=32"] steps: - name: Set image version for stable branch if: startsWith(github.ref, 'refs/heads/stable-') run: | echo "IMG_VER=$(echo ${GITHUB_REF#refs/heads/} | cut -d - -f 2)" >> $GITHUB_ENV - name: Clone the git repo uses: actions/checkout@v2 with: fetch-depth: 0 - name: Pull the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh pull - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh pmemkv-1.5.0/.github/workflows/nightly.yml000066400000000000000000000032441410000423300206000ustar00rootroot00000000000000 name: pmemkv-nightly # It runs non-deafult OSes, listed below with basic set of compilers and tests. on: schedule: # run this job at 01:00 UTC everyday - cron: '0 1 * * *' env: REPO: pmemkv GITHUB_REPO: pmem/pmemkv CONTAINER_REG: ghcr.io/pmem/pmemkv HOST_WORKDIR: ${{ github.workspace }} WORKDIR: utils/docker TEST_TIMEOUT: 900 IMG_VER: latest TYPE: debug PUSH_IMAGE: 1 # use org's Private Access Token to log in to GitHub Container Registry CONTAINER_REG_USER: ${{ secrets.GH_CR_USER }} CONTAINER_REG_PASS: ${{ secrets.GH_CR_PAT }} jobs: linux: name: Linux-nightly runs-on: ubuntu-latest strategy: fail-fast: false matrix: CONFIG: ["OS=centos OS_VER=8", "OS=archlinux-base OS_VER=latest", "OS=debian OS_VER=testing", "OS=debian OS_VER=unstable", "OS=debian OS_VER=latest", "OS=fedora OS_VER=33", "OS=fedora OS_VER=34", "OS=fedora OS_VER=rawhide", "OS=opensuse-leap OS_VER=latest", "OS=opensuse-tumbleweed OS_VER=latest", "OS=ubuntu OS_VER=18.04", "OS=ubuntu OS_VER=21.04", "OS=ubuntu OS_VER=devel"] steps: - name: Clone the git repo uses: actions/checkout@v2 # It rebuilds images every time and pushes them to the container registry - name: Rebuild and push the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh rebuild - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh pmemkv-1.5.0/.github/workflows/weekly.yml000066400000000000000000000025531410000423300204240ustar00rootroot00000000000000 name: pmemkv-weekly # It runs non-day-to-day checks; "unusual" jobs required to be checked only once in a while. on: schedule: # run this job at 01:00 UTC every Saturday - cron: '0 1 * * 6' env: REPO: pmemkv GITHUB_REPO: pmem/pmemkv CONTAINER_REG: ghcr.io/pmem/pmemkv HOST_WORKDIR: ${{ github.workspace }} WORKDIR: utils/docker TEST_TIMEOUT: 900 IMG_VER: latest PUSH_IMAGE: 0 jobs: linux: name: Linux-weekly runs-on: ubuntu-latest strategy: fail-fast: false matrix: CONFIG: ["TYPE=debug OS=ubuntu OS_VER=20.04 TESTS_ASAN=1 TESTS_PMREORDER=0", "TYPE=debug OS=ubuntu OS_VER=20.04 TESTS_UBSAN=1 TESTS_PMREORDER=0", "TYPE=valgrind OS=fedora OS_VER=rawhide TESTS_PMREORDER=0", "TYPE=memcheck_drd OS=fedora OS_VER=rawhide TESTS_PMREORDER=0", "TYPE=building OS=fedora OS_VER=rawhide", "TYPE=building OS=ubuntu OS_VER=devel"] steps: - name: Clone the git repo uses: actions/checkout@v2 # It rebuilds images every time (push is not neccessary; they are pushed in other workflows) - name: Rebuild the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh rebuild - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh pmemkv-1.5.0/.gitignore000066400000000000000000000001061410000423300147640ustar00rootroot00000000000000.* !.gitignore !.gitattributes !.github/ !.clang-format *.d build/ *~ pmemkv-1.5.0/CMakeLists.txt000066400000000000000000000363371410000423300155530ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2021, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv) set(PMEMKV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) include(${PMEMKV_ROOT_DIR}/cmake/helpers.cmake) set_version(VERSION) # ----------------------------------------------------------------- # ## CMake build options # ----------------------------------------------------------------- # option(BUILD_DOC "build documentation" ON) option(BUILD_EXAMPLES "build examples" ON) option(BUILD_TESTS "build tests" ON) option(BUILD_JSON_CONFIG "build the 'libpmemkv_json_config' library" ON) option(TESTS_LONG "enable long running tests" OFF) option(TESTS_USE_FORCED_PMEM "run tests with PMEM_IS_PMEM_FORCE=1 - it speeds up tests execution on emulated pmem" OFF) option(TESTS_USE_VALGRIND "enable tests with valgrind (fail build if Valgrind not found)" ON) option(TESTS_PMEMOBJ_DRD_HELGRIND "enable test of pmemobj engines under drd and helgrind (they should only be run on PMEM)" OFF) option(TESTS_JSON "enable tests which require libpmemkv_json_config library (BUILD_JSON_CONFIG has to be ON)" ON) option(COVERAGE "enable collecting of coverage data" OFF) option(DEVELOPER_MODE "enable developer's checks" OFF) option(CHECK_CPP_STYLE "check code style of C++ sources" OFF) option(USE_ASAN "enable AddressSanitizer (debugging)" OFF) option(USE_UBSAN "enable UndefinedBehaviorSanitizer (debugging)" OFF) option(USE_CCACHE "use ccache if it is available in the system" ON) # Each engine can be enabled separately. option(ENGINE_CMAP "enable cmap engine" ON) option(ENGINE_VCMAP "enable vcmap engine" ON) option(ENGINE_VSMAP "enable vsmap engine" ON) option(ENGINE_CSMAP "enable experimental csmap engine (requires CXX_STANDARD to be set to value >= 14)" OFF) option(ENGINE_STREE "enable experimental stree engine" ON) option(ENGINE_TREE3 "enable experimental tree3 engine" OFF) option(ENGINE_RADIX "enable experimental radix engine" OFF) option(ENGINE_ROBINHOOD "enable experimental robinhood engine (requires CXX_STANDARD to be set to value >= 14)" OFF) option(ENGINE_DRAM_VCMAP "enable testing dram_vcmap engine" OFF) # ----------------------------------------------------------------- # ## Set required and useful variables # ----------------------------------------------------------------- # set(CXX_STANDARD 11 CACHE STRING "C++ language standard") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD ${CXX_STANDARD}) # Specify and print the build type set(DEFAULT_BUILD_TYPE "RelWithDebInfo") set(predefined_build_types Debug Release RelWithDebInfo MinSizeRel) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING "choose the type of build (${predefined_build_types})" FORCE) message(STATUS "CMAKE_BUILD_TYPE not set, setting the default one: ${CMAKE_BUILD_TYPE}") else() message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") if(NOT CMAKE_BUILD_TYPE IN_LIST predefined_build_types) message(WARNING "Unusual build type was set, please make sure it's proper one. " "By default supported are only following: ${predefined_build_types}.") endif() endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PMEMKV_ROOT_DIR}/cmake) set(LIBPMEMOBJ_CPP_REQUIRED_VERSION 1.13.0) set(LIBPMEMOBJ_REQUIRED_VERSION 1.9.1) set(LIBPMEM_REQUIRED_VERSION 1.7) set(MEMKIND_REQUIRED_VERSION 1.8.0) set(RAPIDJSON_REQUIRED_VERSION 1.0.0) set(PKG_CONFIG_REQUIRES) set(DEB_DEPENDS) set(RPM_DEPENDS) set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test CACHE STRING "working directory for tests") message(STATUS "TEST_DIR set to: \"${TEST_DIR}\"") # Do not treat include directories from the interfaces # of consumed Imported Targets as SYSTEM by default. set(CMAKE_NO_SYSTEM_FROM_IMPORTED 1) 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 src/iterator.h src/iterator.cc ) # Add each engine source separately if(ENGINE_CMAP) list(APPEND SOURCE_FILES src/engines/cmap.h src/engines/cmap.cc ) endif() if(ENGINE_CSMAP) list(APPEND SOURCE_FILES src/engines-experimental/csmap.h src/engines-experimental/csmap.cc ) endif() if(ENGINE_VCMAP) list(APPEND SOURCE_FILES src/engines/basic_vcmap.h 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_STREE) list(APPEND SOURCE_FILES src/engines-experimental/stree.h src/engines-experimental/stree.cc src/engines-experimental/stree/persistent_b_tree.h ) endif() if(ENGINE_TREE3) list(APPEND SOURCE_FILES src/engines-experimental/tree3.h src/engines-experimental/tree3.cc ) endif() if(ENGINE_RADIX) list(APPEND SOURCE_FILES src/engines-experimental/radix.h src/engines-experimental/radix.cc ) endif() if(ENGINE_ROBINHOOD) list(APPEND SOURCE_FILES src/engines-experimental/robinhood.h src/engines-experimental/robinhood.cc src/fast_hash.h src/fast_hash.cc ) endif() if(ENGINE_DRAM_VCMAP) list(APPEND SOURCE_FILES src/engines/basic_vcmap.h src/engines-testing/dram_vcmap.h src/engines-testing/dram_vcmap.cc ) endif() # ----------------------------------------------------------------- # ## Setup defines and check status of each engine # ----------------------------------------------------------------- # if(ENGINE_CMAP) add_definitions(-DENGINE_CMAP) message(STATUS "CMAP engine is ON") else() message(STATUS "CMAP engine is OFF") endif() if(ENGINE_CSMAP) add_definitions(-DENGINE_CSMAP) message(STATUS "CSMAP engine is ON") if(CXX_STANDARD LESS 14) message(FATAL_ERROR "CXX_STANDARD must be >= 14 if ENGINE_CSMAP is ON") endif() else() message(STATUS "CSMAP 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_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() if(ENGINE_RADIX) add_definitions(-DENGINE_RADIX) message(STATUS "RADIX engine is ON") else() message(STATUS "RADIX engine is OFF") endif() if(ENGINE_ROBINHOOD) add_definitions(-DENGINE_ROBINHOOD) message(STATUS "ROBINHOOD engine is ON") if(CXX_STANDARD LESS 14) message(FATAL_ERROR "CXX_STANDARD must be >= 14 if ENGINE_ROBINHOOD is ON") endif() else() message(STATUS "ROBINHOOD engine is OFF") endif() if(ENGINE_DRAM_VCMAP) add_definitions(-DENGINE_DRAM_VCMAP) message(STATUS "DRAM_VCMAP engine is ON") else() message(STATUS "DRAM_VCMAP engine is OFF") endif() # ----------------------------------------------------------------- # ## Set compiler's flags # ----------------------------------------------------------------- # if(DEVELOPER_MODE) add_common_flag(-Werror) endif() if(USE_ASAN) add_sanitizer_flag(address) endif() if(USE_UBSAN) add_sanitizer_flag(undefined) endif() if(COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -coverage") 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(-Wno-maybe-uninitialized) add_common_flag(-ggdb DEBUG) add_common_flag(-DDEBUG DEBUG) add_common_flag("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" RELEASE) # ----------------------------------------------------------------- # ## Setup environment, find packages, ## configure list of package dependencies # ----------------------------------------------------------------- # find_package(PkgConfig QUIET) include(FindPerl) include(ExternalProject) include(FindThreads) include(CheckCXXSourceCompiles) include(GNUInstallDirs) 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})") # Configure the ccache as compiler launcher find_program(CCACHE_FOUND ccache) if(USE_CCACHE AND CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) 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 OR ENGINE_DRAM_VCMAP) include(tbb) add_definitions(-DTBB_DEFINE_STD_HASH_SPECIALIZATIONS) list(APPEND PKG_CONFIG_REQUIRES tbb) list(APPEND RPM_DEPENDS tbb) list(APPEND DEB_DEPENDS libtbb2) endif() # ----------------------------------------------------------------- # ## Link libraries, setup targets # ----------------------------------------------------------------- # add_library(pmemkv SHARED ${SOURCE_FILES}) set_target_properties(pmemkv PROPERTIES SOVERSION 1) target_link_libraries(pmemkv PRIVATE -Wl,--version-script=${PMEMKV_ROOT_DIR}/src/libpmemkv.map) target_include_directories(pmemkv PRIVATE src/valgrind) # Enable libpmemobj-cpp valgrind annotations target_compile_options(pmemkv PRIVATE -DLIBPMEMOBJ_CPP_VG_ENABLED=1) target_link_libraries(pmemkv PRIVATE ${LIBPMEMOBJ++_LIBRARIES}) if(ENGINE_VSMAP OR ENGINE_VCMAP) target_link_libraries(pmemkv PRIVATE ${MEMKIND_LIBRARIES}) endif() if(ENGINE_VCMAP OR ENGINE_DRAM_VCMAP) target_link_libraries(pmemkv PRIVATE ${TBB_LIBRARIES}) endif() # ----------------------------------------------------------------- # ## Setup additional targets # ----------------------------------------------------------------- # add_custom_target(checkers ALL) add_custom_target(cppstyle) add_custom_target(cppformat) add_custom_target(check-whitespace) add_custom_target(check-license COMMAND ${PMEMKV_ROOT_DIR}/utils/check_license/check-headers.sh ${PMEMKV_ROOT_DIR} BSD-3-Clause) add_custom_target(copyright-format COMMAND ${PMEMKV_ROOT_DIR}/utils/check_license/check-headers.sh ${PMEMKV_ROOT_DIR} BSD-3-Clause -d) if(CHECK_CPP_STYLE) # check for required programs for code style check and add dependency to ALL find_program(CLANG_FORMAT NAMES clang-format clang-format-9 clang-format-9.0) set(CLANG_FORMAT_REQUIRED "9.0") if(CLANG_FORMAT) get_program_version_major_minor(${CLANG_FORMAT} CLANG_FORMAT_VERSION) message(STATUS "Found clang-format: ${CLANG_FORMAT} (version: ${CLANG_FORMAT_VERSION})") if(NOT (CLANG_FORMAT_VERSION VERSION_EQUAL CLANG_FORMAT_REQUIRED)) message(FATAL_ERROR "required clang-format version is ${CLANG_FORMAT_REQUIRED}") endif() else() message(FATAL_ERROR "CHECK_CPP_STYLE=ON, but clang-format not found (required version: ${CLANG_FORMAT_REQUIRED})") endif() add_dependencies(checkers cppstyle) endif() if(DEVELOPER_MODE) # check for required programs for whitespace and license checks and add dependencies to ALL 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 check-whitespace) add_dependencies(checkers check-license) endif() add_cppstyle(src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/comparator/*.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(main ${PMEMKV_ROOT_DIR}/utils/check_whitespace ${CMAKE_CURRENT_SOURCE_DIR}/utils/check_license/*.sh ${CMAKE_CURRENT_SOURCE_DIR}/*.md) add_check_whitespace(src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/src/comparator/*.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 sub-directories if builds enabled # ----------------------------------------------------------------- # if(BUILD_JSON_CONFIG) include(rapidjson) list(APPEND PKG_CONFIG_REQUIRES "RapidJSON >= ${RAPIDJSON_REQUIRED_VERSION}") list(APPEND RPM_DEPENDS "rapidjson-devel >= ${RAPIDJSON_REQUIRED_VERSION}") list(APPEND DEB_DEPENDS "rapidjson-dev (>= ${RAPIDJSON_REQUIRED_VERSION})") 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=${PMEMKV_ROOT_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_CURRENT_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() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() if(BUILD_DOC) add_subdirectory(doc) endif() # ----------------------------------------------------------------- # ## Configure make install/uninstall and packages # ----------------------------------------------------------------- # string(REPLACE ";" " " PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES}") string(REPLACE ";" ", " RPM_PACKAGE_REQUIRES "${RPM_DEPENDS}") string(REPLACE ";" ", " DEB_PACKAGE_REQUIRES "${DEB_DEPENDS}") configure_file(libpmemkv.pc.in libpmemkv.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpmemkv.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) set_target_properties(pmemkv PROPERTIES PUBLIC_HEADER "src/libpmemkv.h;src/libpmemkv.hpp") install(TARGETS pmemkv PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) configure_file( "${PMEMKV_ROOT_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) if(NOT "${CPACK_GENERATOR}" STREQUAL "") include(${PMEMKV_ROOT_DIR}/cmake/packages.cmake) endif() pmemkv-1.5.0/CONTRIBUTING.md000066400000000000000000000231161410000423300152330ustar00rootroot00000000000000# 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) - [Extending Public API](#extending-public-api) - [Configuring GitHub fork](#configuring-github-fork) # 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. If you add a new feature, implement a new engine or fix a critical bug please append appropriate entry to ChangeLog under newest release. **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 ``` # Adding new dependency Adding each new dependency (including new docker image and package) should be done in a separate commit. The commit message should be: ``` New dependency: dependency_name license: SPDX license tag origin: https://dependency_origin.com ``` # 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 * For this example: `mytree` ### 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 * implement engine interface with its factory * register engine factory (e.g. look at the blackhole) ### Providing Unit Test * Select testcases (based on your engine's capabilities) you wish to use from `tests/engine_scenarios` (or, optionally, create your own) * Write `.cmake` scripts for running selected testcases and put them in `tests/engines` directory. If engine is based on pmemobj or memkind you can just use scripts from `tests/engines/pmemobj_based` or `tests/engines/memkind_based` * If extraordinary tests are required, consider adding them in `tests/engines` * Update `tests/wrong_engine_name_test.cc` test with the new engine * In `tests/CMakeLists.txt`: * Check if all new files are covered by patterns for cppstyle and whitespace checks * Add selected testcases to `tests/CMakeLists.txt` using `add_engine_test` function (see for comparison, current sections for testcases of existing engines) * If engine-specific tests were written, build and add them separately (e.g. using `build_test` and `add_test_generic` functions defined in `tests/ctest_helpers.cmake`) ### 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, 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 * CMake build and `make test` should complete without any errors now ### Updating Common Source * In script(s) executed in CIs (at least in `utils/docker/run-test-building.sh` and `utils/docker/images/install-bindings-dependencies.sh`) add a check/build for new engine * 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. ### 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 `doc/ENGINES-experimental.md`, add `mytree` section # Extending Public API When adding a new public function, you have to make sure to update: - manpages `libpmemkv.3` or `libpmemkv_config.3` in `doc` dir - `doc/CMakeLists.txt` with new manpage link - map file with debug symbols (`src/libpmemkv.map`) - source and header files (incl. libpmemkv.h, libpmemkv.cc, libpmemkv.hpp) - engines' sources if needed (in `src/engines`) - appropriate examples, to show usage - tests (incl. compatibility, c_api and more...) - ChangeLog with new entry for next release # Configuring GitHub fork To build and submit documentation as an automatically generated pull request, the repository has to be properly configured. * [Personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) for GitHub account has to be generated. * Such personal access token has to be set in pmemkv repository's [secrets](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) as `DOC_UPDATE_GITHUB_TOKEN` variable. * `DOC_UPDATE_BOT_NAME` secret variable has to be set. In most cases it will be the same as GitHub account name. * `DOC_REPO_OWNER` secret variable has to be set. Name of the GitHub account, which will be target to make an automatic pull request with documentation. In most cases it will be the same as GitHub account name. To enable automatic images pushing to GitHub Container Registry, following variables: * `CONTAINER_REG` existing environment variable (defined in workflow files, in .github/ directory) has to be updated to contain proper GitHub Container Registry address (to forking user's container registry), * `GH_CR_USER` secret variable has to be set up - an account (with proper permissions) to publish images to the Container Registry (tab **Packages** in your GH profile/organization). * `GH_CR_PAT` secret variable also has to be set up - Personal Access Token (with only read & write packages permissions), to be generated as described [here](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token#creating-a-token) for selected account (user defined in above variable). pmemkv-1.5.0/ChangeLog000066400000000000000000000173431410000423300145610ustar00rootroot00000000000000Tue Jul 27 2021 Igor Chorążewicz * Version 1.5.0 New features: - Introduce (optional) caching layer in radix engine. It can be enabled via config parameters - Add new config's flag - 'create_if_missing' to address the missing functionality. If set, pmemkv tries to open the pool and creates it if missing. It's mutually exclusive with 'create_or_error_if_exists' flag Bug fixes: - Return correct status for wrong parameters in pmemkv_config_from_json (instead of failing on assert) - Fix iterator::key() implementation to return the whole key, not only the part up to the first null-terminator - Return correct status from memkind-based engines on open failure Other changes: - Add alias 'create_or_error_if_exists' for 'force_create' config's flag and deprecate the latter one. The old flag can still be used and it behaves with no difference. It's discouraged from using, though - Add dram_vcmap engine based on tbb::concurrent_hash_map for benchmarking purposes - Switch default CMake's build type from "Debug" to "RelWithDebInfo" when compiling from sources - it's relevant for a GitHub users (who may e.g. run some benchmarks) Tue Jul 06 2021 Łukasz Stolarczuk * Version 1.2.1 This is the last bugfix release for pmemkv 1.2 version. Maintenance of this version is no longer supported. Notable changes: - fix of missing checks in move assigment operators - stree: fix operator== by adding missing const specifier - stree: fix missing check in node_iterator operator= - json_config: return error code for wrong parameters, instead of asserting them - json config: extend error messages Mon Jun 28 2021 Łukasz Stolarczuk * Version 1.1.1 This is the last bugfix release for pmemkv 1.1 version. Maintenance of this version is no longer supported. Notable changes: - json_config: return error status for improper parameters instead of asserting nullptr (and crashing) - fix error messages for NOT_FOUND and STOPPED_BY_CB statuses (which are not errors) by omitting setting (error) message from the previous status - vcmap: fix put() implementation (to be thread-safe) and decrease the number of temporary string allocations - vcmap: fix value_type for concurrent_hash_map - vcmap: fix to work with the latest TBB (2021.1) #894 - stree: fix operator== - clarify and extend documentation Mon Feb 15 2021 Igor Chorążewicz * Version 1.4 Features: - new experimental engine - robinhood (concurrent, unsorted, persistent map) - experimental iterators API which allows to directly modify values (or parts of values) - experimental transaction API which allows grouping `put` and `remove` operations into a single atomic action Other changes: - optimized put() in radix engine Major fixes: - calling pmemkv_errormsg() now returns empty message for NOT_FOUND and STOPPED_BY_CB (instead of a possible previous, unrelated error) - fix vcmap OOM error on put (github issue #798) Known issues: - vcmap (github issue #894) Tue Oct 06 2020 Szymon Romik * Version 1.0.3 This is the last bugfix release for pmemkv 1.0 version. Maintenance of this version is no longer supported. Notable changes: - VSmap::put method refactor (to use emplace) - add errormsg() to db class Fri Oct 02 2020 Szymon Romik * Version 1.3 This release introduces a new experimental engine - radix (single-threaded sorted map, backed by libpmemobj-cpp's radix_tree container). We have also extended configuration class API and redesigned optimized version of stree engine. Features: - radix engine (single-threaded sorted map) - config setters with type safety for common config fields - stree engine optimization (single-threaded sorted map with custom comparator support) Major fixes: - fixed operator== in stree engine - fixed missing checks in move assignment operators Other changes: - operator<< for pmem::kv::status Known issues: - vcmap (github issues #623, #798) Fri May 29 2020 Szymon Romik * Version 1.2 This release introduces a new experimental engine - csmap (concurrent sorted map, backed by libpmemobj-cpp's concurrent_map). We have also provided support for custom comparators for sorted engines. Features: - csmap engine (concurrent sorted map) - support for custom comparator for sorted engines Optimizations: - vsmap::put() now uses emplace() internally Other changes: - test framework refactor (scenario-based tests, extednded tests for concurrentcy and sorting) Fri Feb 07 2020 Igor Chorążewicz * Version 1.0.2 This is a bugfix release for pmemkv 1.0 This release: - fixes misleading information in manpages - fixes compilation of vsmap engine on newer compilers - introduces requirement on size of std::string to be less than or equal to 32 Fri 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.5.0/INSTALLING.md000066400000000000000000000157261410000423300150000ustar00rootroot00000000000000# Installing pmemkv Installation of Key-Value Datastore for Persistent Memory ## Contents 1. [Building from Sources](#building-from-sources) - [Prerequisites](#prerequisites) - [Building pmemkv and running tests](#building-pmemkv-and-running-tests) - [Managing shared library](#managing-shared-library) - [Out-of-source builds](#out-of-source-builds) 2. [Installing on Fedora](#installing-on-fedora) 3. [Installing on Ubuntu](#installing-on-ubuntu) 4. [Using Experimental Engines](#using-experimental-engines) 5. [Building packages](#building-packages) 6. [Using a Pool Set](#using-a-pool-set) ## Building from Sources ### Prerequisites * **Linux 64-bit** (OSX and Windows are not yet supported) * **libpmem** and **libpmemobj**, which are part of [PMDK](https://github.com/pmem/pmdk) - Persistent Memory Development Kit 1.9.1 * [**libpmemobj-cpp**](https://github.com/pmem/libpmemobj-cpp) - C++ PMDK bindings 1.13.0 * [**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 1.0.0 (required by `libpmemkv_json_config` helper library) * Used only for **testing**: * [**pmempool**](https://github.com/pmem/pmdk/tree/master/src/tools/pmempool) - pmempool utility, part of PMDK * [**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). * Used only for **development**: * [**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 Required packages (or their names) for some OSes may differ. Some examples or scripts in this repository may require additional dependencies, but should not interrupt the build. See our **[Dockerfiles](utils/docker/images)** (used e.g. on our CI systems) to get an idea what packages are required to build the entire pmemkv, with all tests and examples. ### Building pmemkv and running tests ```sh git clone https://github.com/pmem/pmemkv cd pmemkv mkdir ./build cd ./build cmake .. -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug # run CMake, prepare Debug version make -j$(nproc) # build everything make test # run all tests ``` To see the output of failed tests, instead of the last command (`make test`) you can run: ```sh ctest --output-on-failure ``` 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), instead of a standard `cmake ..` command run: ```sh cmake .. -DBUILD_JSON_CONFIG=OFF ``` ### Managing shared library To package `pmemkv` as a shared library and install on your system: ```sh cmake .. [-DCMAKE_BUILD_TYPE=Release] # prepare e.g. Release version 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 [-DCMAKE_BUILD_TYPE=Release] 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 -j$(nproc) make test # or 'ctest --output-on-failure' ``` ## Installing on Fedora Install required packages (see comprehensive list of packages used in our CI on a Fedora image in [utils directory](./utils/docker/images/)): 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 -j$(nproc) 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 -j$(nproc) 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 (see comprehensive list of packages used in our CI on a Ubuntu image in [utils directory](./utils/docker/images/)): ```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 -j$(nproc) 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 -j$(nproc) 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_CSMAP=ON ``` Now build will contain selected experimental engine(s) and their dependencies, that are not available by default. Experimental engines may require additional libraries, see **prerequisites** section of a selected engine in [ENGINES-experimental.md](doc/ENGINES-experimental.md). ## Building packages ```sh ... cmake .. -DCPACK_GENERATOR="$GEN" -DCMAKE_INSTALL_PREFIX=/usr [-DCMAKE_BUILD_TYPE=Release] make -j$(nproc) 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.5.0/LICENSE000066400000000000000000000036041410000423300140070ustar00rootroot00000000000000SPDX-License-Identifier: BSD-3-Clause Copyright 2017-2021, 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. * src/fast_hash.h and src/fast_hash.cc are covered by MIT license, contained in those files. pmemkv-1.5.0/README.md000066400000000000000000000135021410000423300142570ustar00rootroot00000000000000# **pmemkv** [![GHA build status](https://github.com/pmem/pmemkv/workflows/pmemkv/badge.svg?branch=master)](https://github.com/pmem/pmemkv/actions) [![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) [![PMEMKV version](https://img.shields.io/github/tag/pmem/pmemkv.svg)](https://github.com/pmem/pmemkv/releases/latest) [![Packaging status](https://repology.org/badge/tiny-repos/pmemkv.svg)](https://repology.org/project/pmemkv/versions) `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, including **C API** and **C++ API** see: https://pmem.io/pmemkv. Documentation is available for every branch/release. For most recent always see (**master** branch): * [C++ docs](https://pmem.io/pmemkv/master/doxygen/index.html), * [C manpage libpmemkv(3)](https://pmem.io/pmemkv/master/manpages/libpmemkv.3.html). Latest releases can be found on the ["releases" tab](https://github.com/pmem/pmemkv/releases). Up-to-date, current support/maintenance status of branches/releases is available on [pmem.io](https://pmem.io/pmemkv/index.html#releases-support-status). There is also a small helper library `pmemkv_json_config` provided. See its [manual](doc/libpmemkv_json_config.3.md) for details. ## Table of contents 1. [Installation](#installation) 2. [Language Bindings](#language-bindings) - [C/C++ Examples](#cc-examples) - [Other Languages](#other-languages) 3. [Storage Engines](#storage-engines) 4. [Benchmarks](#benchmarks) 5. [Contact us](#contact-us) ## 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 can be used in other languages - Java, Node.js, Python, and Ruby. ![pmemkv-bindings](doc/architecture.png) ### C/C++ Examples Examples for C and C++ can be found within this repository in [examples directory](./examples/). ### Other Languages The above-mentioned 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 * **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 share 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 | Persistent | | ------------ | ----------- | :-------------: | :-----------: | :-------: | :-------: | | [cmap](doc/libpmemkv.7.md#cmap) | Concurrent hash map | No | Yes | No | Yes | | [vsmap](doc/libpmemkv.7.md#vsmap) | Volatile sorted hash map | No | No | Yes | No | | [vcmap](doc/libpmemkv.7.md#vcmap) | Volatile concurrent hash map | No | Yes | No | No | | [csmap](doc/ENGINES-experimental.md#csmap) | [Concurrent sorted map](https://pmem.io/libpmemobj-cpp/master/doxygen/classpmem_1_1obj_1_1experimental_1_1concurrent__map.html) | Yes | Yes | Yes | Yes | | [radix](doc/ENGINES-experimental.md#radix) | [Radix tree](https://pmem.io/libpmemobj-cpp/master/doxygen/classpmem_1_1obj_1_1experimental_1_1radix__tree.html) | Yes | No | Yes | Yes | | [tree3](doc/ENGINES-experimental.md#tree3) | Persistent B+ tree | Yes | No | No | Yes | | [stree](doc/ENGINES-experimental.md#stree) | Sorted persistent B+ tree | Yes | No | Yes | Yes | | [robinhood](doc/ENGINES-experimental.md#robinhood) | Persistent hash map with Robin Hood hashing | Yes | Yes | No | Yes | The production quality engines are described in the [libpmemkv(7)](doc/libpmemkv.7.md#engines) manual and the experimental ones are described in the [ENGINES-experimental.md](doc/ENGINES-experimental.md) file. `pmemkv` also provides testing engines, which may be used in unit tests or for benchmarking application overhead | Engine Name | Description | Experimental | Concurrent | Sorted | Persistent | | ------------ | ----------- | :-------------: | :-----------: | :-------: | :-------: | | [blackhole](doc/libpmemkv.7.md#blackhole) | Accepts everything, returns nothing | No | Yes | No | No | | [dram_vcmap](doc/ENGINES-testing.md#dram_vcmap) | Volatile concurrent hash map placed entirely on DRAM | Yes | Yes | No | No | [Contributing a new engine](CONTRIBUTING.md#creating-new-engines) is easy, so feel encouraged! ## Benchmarks **Experimental** benchmark based on *leveldb*'s [db_bench](https://github.com/google/leveldb/blob/master/benchmarks/db_bench.cc) to measure pmemkv's performance is available here: https://github.com/pmem/pmemkv-bench (previously *pmemkv-tools*). ## Contact us For more information about **pmemkv**, contact Igor Chorążewicz (igor.chorazewicz@intel.com), Piotr Balcer (piotr.balcer@intel.com) or post on our **#pmem** Slack channel using [this invite link](https://join.slack.com/t/pmem-io/shared_invite/enQtNzU4MzQ2Mzk3MDQwLWQ1YThmODVmMGFkZWI0YTdhODg4ODVhODdhYjg3NmE4N2ViZGI5NTRmZTBiNDYyOGJjYTIyNmZjYzQxODcwNDg) or [Google group](https://groups.google.com/group/pmem). pmemkv-1.5.0/VERSION000066400000000000000000000000061410000423300140430ustar00rootroot000000000000001.5.0 pmemkv-1.5.0/cmake/000077500000000000000000000000001410000423300140575ustar00rootroot00000000000000pmemkv-1.5.0/cmake/FindLIBPMEM.cmake000066400000000000000000000011071410000423300167460ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation find_path(LIBPMEM_INCLUDE_DIR libpmem.h) find_library(LIBPMEM_LIBRARY NAMES pmem libpmem) set(LIBPMEM_LIBRARIES ${LIBPMEM_LIBRARY}) set(LIBPMEM_INCLUDE_DIRS ${LIBPMEM_INCLUDE_DIR}) set(MSG_NOT_FOUND "libpmem NOT found (set CMAKE_PREFIX_PATH to point the location)") if(NOT (LIBPMEM_INCLUDE_DIR AND LIBPMEM_LIBRARY)) if(LIBPMEM_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() mark_as_advanced(LIBPMEM_LIBRARY LIBPMEM_INCLUDE_DIR) pmemkv-1.5.0/cmake/FindLIBPMEMOBJ.cmake000066400000000000000000000011641410000423300173040ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2020, Intel Corporation find_path(LIBPMEMOBJ_INCLUDE_DIR libpmemobj.h) find_library(LIBPMEMOBJ_LIBRARY NAMES pmemobj libpmemobj) set(LIBPMEMOBJ_LIBRARIES ${LIBPMEMOBJ_LIBRARY}) set(LIBPMEMOBJ_INCLUDE_DIRS ${LIBPMEMOBJ_INCLUDE_DIR}) set(MSG_NOT_FOUND "libpmemobj NOT found (set CMAKE_PREFIX_PATH to point the location)") if(NOT (LIBPMEMOBJ_INCLUDE_DIR AND LIBPMEMOBJ_LIBRARY)) if(LIBPMEMOBJ_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() mark_as_advanced(LIBPMEMOBJ_LIBRARY LIBPMEMOBJ_INCLUDE_DIR) pmemkv-1.5.0/cmake/cmake_uninstall.cmake.in000066400000000000000000000020501410000423300206340ustar00rootroot00000000000000# 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.5.0/cmake/helpers.cmake000066400000000000000000000134701410000423300165300ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # helpers.cmake - helper functions for top-level CMakeLists.txt # include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) # Sets project's version based on git output, in ${VERSION} function(set_version VERSION) set(VERSION_FILE ${PMEMKV_ROOT_DIR}/VERSION) if(EXISTS ${VERSION_FILE}) file(STRINGS ${VERSION_FILE} FILE_VERSION) set(VERSION ${FILE_VERSION} PARENT_SCOPE) return() endif() execute_process(COMMAND git describe OUTPUT_VARIABLE GIT_VERSION WORKING_DIRECTORY ${PMEMKV_ROOT_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) 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 WORKING_DIRECTORY ${PMEMKV_ROOT_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE) set(VERSION ${GIT_COMMIT} PARENT_SCOPE) 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} ${PMEMKV_ROOT_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${PMEMKV_ROOT_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} ${PMEMKV_ROOT_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${ARGN} ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${PMEMKV_ROOT_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} ${PMEMKV_ROOT_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() # Adds sanitizer flag for CXX compiler. # It's used to check i.a. for USAN/UBSAN sanitizers, as an additional static analysis. macro(add_sanitizer_flag flag) set(SAVED_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} -fsanitize=${flag}") check_cxx_compiler_flag("-fsanitize=${flag}" CXX_HAS_ASAN_UBSAN) if(CXX_HAS_ASAN_UBSAN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${flag}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${flag}") else() message("${flag} sanitizer not supported") endif() set(CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES}) endmacro() pmemkv-1.5.0/cmake/libpmemobj++.cmake000066400000000000000000000007131410000423300173300ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2019, Intel Corporation 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.5.0/cmake/memkind.cmake000066400000000000000000000031421410000423300165050ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2020, Intel Corporation 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 #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.5.0/cmake/packages.cmake000066400000000000000000000050551410000423300166440ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # 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 temporary package") 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 ${PMEMKV_ROOT_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 "igor.chorazewicz@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.5.0/cmake/rapidjson.cmake000066400000000000000000000016061410000423300170550ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2021, Intel Corporation message(STATUS "Checking for module 'RapidJSON'") if(PKG_CONFIG_FOUND) pkg_check_modules(RapidJSON QUIET RapidJSON>=${RAPIDJSON_REQUIRED_VERSION}) endif() if(NOT RapidJSON_FOUND) # find_package (run after pkg-config) without unsetting this var is not working correctly unset(RapidJSON_FOUND CACHE) find_package(RapidJSON ${RAPIDJSON_REQUIRED_VERSION} QUIET REQUIRED) set(RapidJSON_LIBRARIES ${RapidJSON_LIBRARY}) set(RapidJSON_INCLUDE_DIRS ${RapidJSON_INCLUDE_DIR}) message(STATUS " Found in dir '${RapidJSON_DIR}' using CMake's find_package (ver: ${RapidJSON_VERSION})") else() message(STATUS " Found in dir '${RapidJSON_INCLUDEDIR}' using pkg-config (ver: ${RapidJSON_VERSION})") endif() include_directories(${RapidJSON_INCLUDE_DIRS}) link_directories(${RapidJSON_LIBRARY_DIRS}) pmemkv-1.5.0/cmake/tbb.cmake000066400000000000000000000032171410000423300156330ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2021, Intel Corporation set(TBB_DIR "" CACHE PATH "dir with TBBConfig.cmake, to be set if using custom TBB installation") message(STATUS "Checking for module 'tbb'") # TBB was found without pkg-config, warning must be printed # and global variable with tbb libs has to be set. function(handle_tbb_found_no_pkgconf) set(TBB_LIBRARIES ${TBB_IMPORTED_TARGETS} PARENT_SCOPE) message(STATUS " Found in dir '${TBB_DIR}' using CMake's find_package (ver: ${TBB_VERSION})") message(WARNING "TBB package was found without pkg-config. If you'll compile a " "standalone application and link with this libpmemkv, TBB may not be properly linked!") endfunction() # CMake param TBB_DIR is priortized. This is the best to use # while developing/testing pmemkv with custom TBB installation. if(TBB_DIR) message(STATUS " CMake param TBB_DIR is set, look ONLY in there (${TBB_DIR})!") find_package(TBB QUIET NAMES TBB tbb NO_DEFAULT_PATH) if(TBB_FOUND) handle_tbb_found_no_pkgconf() else() message(FATAL_ERROR "TBB_DIR is set, but does not contain a path to TBB. " "Either set this var to a dir with TBBConfig.cmake (or tbb-config.cmake), " "or unset it and let CMake find TBB in the system (using e.g. pkg-config).") endif() else() if(PKG_CONFIG_FOUND) pkg_check_modules(TBB QUIET tbb) if(TBB_FOUND) message(STATUS " Found in dir '${TBB_LIBDIR}' using pkg-config (ver: ${TBB_VERSION})") return() endif() endif() # find_package (run after pkg-config) without unsetting this var is not working correctly unset(TBB_FOUND CACHE) find_package(TBB REQUIRED tbb) handle_tbb_found_no_pkgconf() endif() pmemkv-1.5.0/codecov.yml000066400000000000000000000003501410000423300151420ustar00rootroot00000000000000coverage: status: project: default: threshold: 0.2 ignore: - doc/ - examples/ - utils/ - src/valgrind/ - tests/ comment: layout: "diff, files" behavior: default require_changes: yes pmemkv-1.5.0/doc/000077500000000000000000000000001410000423300135445ustar00rootroot00000000000000pmemkv-1.5.0/doc/CMakeLists.txt000066400000000000000000000157531410000423300163170ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # doc/CMakeLists.txt - prepares 'make doc' command for documentation # # Prepares example code to inject into man page: # Read file pointed by ${PATH}, remove comments (with license and description) # up to the first preprocessor's directive and place it in ${VAL} in a caller scope. function(strip_example PATH VAL) file(READ ${PATH} example_content) string(REGEX REPLACE "([/]+[*/]+).*([*]+[/]+)([\n]+#)" "#" example_content "${example_content}") set(${VAL} "${example_content}" PARENT_SCOPE) endfunction() # Converts md files into manpage format. Requires pandoc command. # NAME is output manpage name, INPUT is a path to the source md file function(configure_man NAME INPUT) add_custom_command(OUTPUT ${MAN_DIR}/${NAME} MAIN_DEPENDENCY ${INPUT} COMMAND ${PMEMKV_ROOT_DIR}/utils/md2man/md2man.sh ${INPUT} ${PMEMKV_ROOT_DIR}/utils/md2man/default.man ${MAN_DIR}/${NAME} ${VERSION}) list(APPEND MANPAGE_OUTFILES ${MAN_DIR}/${NAME}) set(MANPAGE_OUTFILES ${MANPAGE_OUTFILES} PARENT_SCOPE) endfunction() # Generate files (to be installed) - links of C API functions (passed as {ARGN}) # to chosen (3) manpage. function(add_manpage_links MANPAGE) foreach(function ${ARGN}) set(CONTENT ".so ${MANPAGE}") file(WRITE ${MAN_DIR}/${function}.3 "${CONTENT}") endforeach() endfunction() # ----------------------------------------------------------------- # ## Setup custom targets and useful variables # ----------------------------------------------------------------- # set(MANPAGE_OUTFILES "") set(DOXYGEN_COMMAND "") set(MAN_DIR ${CMAKE_CURRENT_BINARY_DIR}/man) add_check_whitespace(man ${CMAKE_CURRENT_SOURCE_DIR}/*.*) # ----------------------------------------------------------------- # ## Prepare C documentation (using manpage format) # ----------------------------------------------------------------- # find_program(PANDOC NAMES pandoc) if(PANDOC) message(STATUS "Found pandoc: ${PANDOC}") ## Manpages without any examples # libpmemkv.7 configure_man(libpmemkv.7 ${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv.7.md) # libpmemkv_json_config.3 configure_man(libpmemkv_json_config.3 ${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv_json_config.3.md) add_manpage_links(libpmemkv_json_config.3 pmemkv_config_from_json pmemkv_config_from_json_errormsg) ## Manpages with example(s) injected # libpmemkv.3 strip_example(${PMEMKV_ROOT_DIR}/examples/pmemkv_basic_c/pmemkv_basic.c C_EXAMPLE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv.3.md.in ${MAN_DIR}/tmp/libpmemkv.3.md) configure_man(libpmemkv.3 ${MAN_DIR}/tmp/libpmemkv.3.md) add_manpage_links(libpmemkv.3 pmemkv_get_kv_callback pmemkv_get_v_callback pmemkv_open pmemkv_close pmemkv_count_all pmemkv_count_above pmemkv_count_below pmemkv_count_between pmemkv_get_all pmemkv_get_above pmemkv_get_below pmemkv_get_between pmemkv_exists pmemkv_get pmemkv_get_copy pmemkv_put pmemkv_remove pmemkv_defrag pmemkv_errormsg) # libpmemkv_config.3 strip_example( ${PMEMKV_ROOT_DIR}/examples/pmemkv_config_c/pmemkv_config.c CONFIG_TYPE_BASED_EXAMPLE) strip_example( ${PMEMKV_ROOT_DIR}/examples/pmemkv_config_c/pmemkv_basic_config.c CONFIG_BASIC_EXAMPLE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv_config.3.md.in ${MAN_DIR}/tmp/libpmemkv_config.3.md) configure_man(libpmemkv_config.3 ${MAN_DIR}/tmp/libpmemkv_config.3.md) add_manpage_links(libpmemkv_config.3 pmemkv_config_new pmemkv_config_delete pmemkv_config_put_size pmemkv_config_put_path pmemkv_config_put_force_create pmemkv_config_put_create_or_error_if_exists pmemkv_config_put_create_if_missing pmemkv_config_put_comparator pmemkv_config_put_oid pmemkv_config_put_data pmemkv_config_put_object pmemkv_config_put_object_cb pmemkv_config_put_uint64 pmemkv_config_put_int64 pmemkv_config_put_string pmemkv_config_get_data pmemkv_config_get_object pmemkv_config_get_uint64 pmemkv_config_get_int64 pmemkv_config_get_string pmemkv_comparator_new pmemkv_comparator_delete) # libpmemkv_tx.3 strip_example( ${PMEMKV_ROOT_DIR}/examples/pmemkv_transaction_c/pmemkv_transaction.c TRANSACTION_BASIC_C_EXAMPLE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv_tx.3.md.in ${MAN_DIR}/tmp/libpmemkv_tx.3.md) configure_man(libpmemkv_tx.3 ${MAN_DIR}/tmp/libpmemkv_tx.3.md) add_manpage_links(libpmemkv_tx.3 pmemkv_tx_begin pmemkv_tx_put pmemkv_tx_remove pmemkv_tx_commit pmemkv_tx_abort pmemkv_tx_end) # libpmemkv_iterator.3 strip_example( ${PMEMKV_ROOT_DIR}/examples/pmemkv_iterator_c/pmemkv_iterator.c ITERATOR_BASIC_EXAMPLE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv_iterator.3.md.in ${MAN_DIR}/tmp/libpmemkv_iterator.3.md) configure_man(libpmemkv_iterator.3 ${MAN_DIR}/tmp/libpmemkv_iterator.3.md) add_manpage_links(libpmemkv_iterator.3 pmemkv_iterator_new pmemkv_write_iterator_new pmemkv_iterator_delete pmemkv_write_iterator_delete pmemkv_iterator_seek pmemkv_iterator_seek_lower pmemkv_iterator_seek_lower_eq pmemkv_iterator_seek_higher pmemkv_iterator_seek_higher_eq pmemkv_iterator_seek_to_first pmemkv_iterator_seek_to_last pmemkv_iterator_is_next pmemkv_iterator_next pmemkv_iterator_prev pmemkv_iterator_key pmemkv_iterator_read_range pmemkv_write_iterator_write_range pmemkv_write_iterator_commit pmemkv_write_iterator_abort) # install manpages install(FILES ${MAN_DIR}/libpmemkv.7 DESTINATION ${CMAKE_INSTALL_MANDIR}/man7) install(DIRECTORY ${MAN_DIR}/ DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 FILES_MATCHING PATTERN "*.3" PATTERN "tmp" EXCLUDE) else() message(WARNING "pandoc not found - man pages (C documentation) will not be generated") endif() # ----------------------------------------------------------------- # ## Prepare C++ documentation (using doxygen format) # ----------------------------------------------------------------- # include(FindDoxygen) if(DOXYGEN_FOUND AND DOXYGEN_DOT_FOUND) if(DEVELOPER_MODE) set(DOXYGEN_WARN_AS_ERROR "YES") else() set(DOXYGEN_WARN_AS_ERROR "NO") endif() set(DOXYGEN_OUTFILE "${CMAKE_CURRENT_BINARY_DIR}/libpmemkv.Doxyfile") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libpmemkv.Doxyfile.in" ${DOXYGEN_OUTFILE} @ONLY) set(DOXYGEN_COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUTFILE}) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cpp_html/ DESTINATION ${CMAKE_INSTALL_DOCDIR}) elseif(NOT DOXYGEN_FOUND) message(WARNING "Doxygen not found - Doxygen (C++) documentation will not be generated") else() message(WARNING "Dot tool not found - Doxygen (C++) documentation will not be generated") endif() # ----------------------------------------------------------------- # ## Prepare the actual 'make doc' command # ----------------------------------------------------------------- # if(MANPAGE_OUTFILES OR DOXYGEN_COMMAND) add_custom_target(doc ALL ${DOXYGEN_COMMAND} DEPENDS ${MANPAGE_OUTFILES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) else() message(WARNING "Neither C or C++ docs could be built. " "If you wish to build them install pandoc (for C) and/or doxygen (for C++), " "otherwise disable it using CMake option -DBUILD_DOC=OFF") endif() pmemkv-1.5.0/doc/ENGINES-experimental.md000066400000000000000000000274601410000423300176620ustar00rootroot00000000000000# Experimental Storage Engines for pmemkv - [tree3](#tree3) - [csmap](#csmap) - [radix](#radix) - [stree](#stree) - [robinhood](#robinhood) # 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 pool (with layout "pmemkv_tree3"), to open or create. + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + type: uint64_t + min value: 8388608 (8MB) For more detailed configuration's description see [cmap section in libpmemkv(7)](libpmemkv.7.md#cmap). ### 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 No additional packages are required. # csmap A persistent, concurrent and sorted engine, backed by a skip list. It is disabled by default. It can be enabled in CMake using the `ENGINE_CSMAP` option (requires C++14 support). All methods of csmap are thread safe. Put, get, count_\* and get_\* scale with the number of threads. Remove method is currently implemented to take a global lock - it blocks all other threads. ### Configuration * **path** -- Path to the database pool (layout "pmemkv_csmap"), to open or create. + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + type: uint64_t For more detailed configuration's description see [cmap section in libpmemkv(7)](libpmemkv.7.md#cmap). ### Prerequisites No additional packages are required. # radix A persistent, sorted (without custom comparator support) engine, backed by a radix tree. It is disabled by default. It can be enabled in CMake using the `ENGINE_RADIX` option. It is possible to enable DRAM caching layer for radix engine (for details, see Configuration section). Enabling DRAM caching can improve write latency as new elements are appended to a pmem-resident log and inserted to a DRAM index instead of modifying radix tree in-place. Elements from pmem-resident log are transferred to a radix tree by a background thread. DRAM index is implemented as an LRU cache with maximum size set by the user. ### Configuration * **path** -- Path to the database pool (layout "pmemkv_radix"), to open or create. + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + type: uint64_t * **dram_caching** - If 1, enables DRAM caching layer on top of radix tree. If enabled, **cache_size** and **log_size** parameters must be set. + type: uint64_t + default value: 0 * **cache_size** - Only needed if **dram_caching** is set. Specifies maximum number of elements which can be held in DRAM index. + type: uint64_t + default value: 1000000 * **log_size** - Only needed if **dram_caching** is set. Specifies size of PMEM-resident log in bytes. + type: uint64_t + default value: 64000000 For more detailed configuration's description see [cmap section in libpmemkv(7)](libpmemkv.7.md#cmap). ### Prerequisites No additional packages are 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 pool (layout "pmemkv_stree"), to open or create. + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + type: uint64_t For more detailed configuration's description see [cmap section in libpmemkv(7)](libpmemkv.7.md#cmap). ### Internals (TBD) ### Prerequisites No additional packages are required. # robinhood A persistent and concurrent engine, backed by a hash table with Robin Hood hashing (some [info](https://www.sebastiansylvan.com/post/robin-hood-hashing-should-be-your-default-hash-table-implementation/) about the algorithm). It uses only fixed size keys and values (8 bytes). It is disabled by default. It can be enabled in CMake using the `ENGINE_ROBINHOOD` option. There are two parameters to be optionally modified by env variables: * **PMEMKV_ROBINHOOD_LOAD_FACTOR** -- load factor to indicate resize threshold * **PMEMKV_ROBINHOOD_SHARDS_NUMBER** -- number of shards within the engine ### Configuration * **path** -- Path to the database pool (layout "pmemkv_robinhood"), to open or create. + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + type: uint64_t For more detailed configuration's description see [cmap section in libpmemkv(7)](libpmemkv.7.md#cmap). ### Prerequisites No additional 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! # Archived engines **caching** It was present in releases <= 1.2 That engine was using a sub engine (one of other engines) to cache requests to external Redis or Memcached server. pmemkv-1.5.0/doc/ENGINES-testing.md000066400000000000000000000010141410000423300166250ustar00rootroot00000000000000# Testing Storage Engines for pmemkv - [dram_vcmap](#dram_vcmap) # dram_vcmap A volatile concurrent engine backed by std::allocator. Data written using this engine is stored entierly in DRAM and lost after the database is closed. This engine is variant of vcmap, which uses std::allocator to allocate memory. That means it is built on top of tbb::concurrent\_hash\_map data structure; std::basic\_string is used as a type of a key and a value. TBB package is required. This engine do not require any config parameters pmemkv-1.5.0/doc/RELEASE_STEPS.md000066400000000000000000000032411410000423300161240ustar00rootroot00000000000000## pmemkv release steps This document contains all the steps required to make a new release of pmemkv. \#define $VERSION = current full version (e.g. 1.0.2); $VER = major+minor only version (e.g. 1.0) Make a release locally: - add an entry to ChangeLog, remember to change the day of the week in the release date - for major/minor releases mention compatibility with the previous release - echo $VERSION > VERSION - git add VERSION - git commit -a -S -m "$VERSION release" - git tag -a -s -m "Version $VERSION" $VERSION Undo temporary release changes: - git rm VERSION - git commit -m "Remove VERSION file" - for major/minor release: - create stable-$VER branch now: git checkout -b stable-$VER Publish changes: - for major/minor release: - git push upstream HEAD:master $VERSION - create and push to upstream stable-$VER branch - for patch release: - git push upstream HEAD:stable-$VER $VERSION - create PR from stable-$VER to next stable (or master, if release is from most recent stable branch) Publish package and make it official: - go to [GitHub's releases tab](https://github.com/pmem/pmemkv/releases/new): - tag version: $VERSION, release title: pmemkv version $VERSION, description: copy entry from ChangeLog and format it with no tabs and no characters limit in line - announce the release on pmem group and on pmem slack channel(s) Later, for major/minor release: - add new version in compatibility test in run-compatibility.sh, on stable-$VER branch - once gh-pages branch contains new documentation: - add there (in index.md) new links to manpages and Doxygen docs - update there "Releases' support status" table (update any other release's status if needed) pmemkv-1.5.0/doc/architecture.png000066400000000000000000003067441410000423300167520ustar00rootroot00000000000000PNG  IHDRL pHYs.#.#x?vtIME  hrQ IDATx^y|T?sN& Ke■ m-pZx۪h-@k/pumK(pA(%IH233g33|ޯ׼Ȝ9Ϝws """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""OہȌ+ m__Zn($ہ )D6ܬ,*e 煈(z;%V"""(ZS=/b]6Ei*aQ""">VEQ]]]j0@g """j0!""#\ :m 煈(zXaBDDD1(t} QpW""""""""7aBDDDV! )%P)wtBUgMPlTr}+ 2UP(0)$vR蜙cA&4uP6M\&s m"A"Kf]RVrY~,f⊂$ B>H}`6U]2:/vϚ %r NePDG1\Zs @@6)!]h;;e2UN%P)F5"""#DDD]qEA"r)2Օrtꄔ痖WdR.,/-O4Y;`I^J,#NqEA,Bx:!BMZh/}`4 -+Y=7s^;; EOɌU<őUʩK++ rT!bޱ>qEA(J)y!9DDD!(([UYA"4LR~`dJE𙽳!>Sb5|,D8S\Q Q!X%)|fm]7➮(bkm`@WM(?Ծ ,Vc]*^* ϟvCDD(V ^XQ\Qo7Y֖Pi`i}]JW:N=0l?A-T SJf*B)Ti0z S g KrËXaBDD5r K`?{mB$0/ZoAvuHPXHw(Y_Z7RTK~>WY6kK׍HBJ zxHFʍxhQ XaBDDE#_O= (#䕈KJr)2/<_C+OX-+[Wf2(HIU I)RNJk2WH9UJ @r{p@rr b@(O„ᥲAJd}P0FbΪY3 _3őtM@*hBLp&lj$djQ^Iڒv~wHII:DDظJQ223PWW$t}KW,Lpu8k+|o[_Zz o|X`v_upxf,kkr//+I jp?m[!{[uVEX[˽ 3 proDX9FۍD8ae|2ʒ` 7~|V/CsevBRy4 Qzbi]""/CDDOWLSEIR  EQ#`zl92h'\\&r5#omT; La0lH1$|L!VI)r*UīEy|""„ȄAi_]RJIBfZ@ќ^:qEYF{41(U+D+0ރgUEJ=0WWH)WyVFDD(!"" ӻgDǁ& 2:upYS=/<XV 7T3<'(WOQ~J!TV #(^]Yq0+Qc QVVΚ#|˕ʥi/L q@GD8 w]%jM̧7|fD$oh[_Z$$RΒ@lg"""#CDD9mhRi4Bo… 9 1;<%Z TrܷO=pv^0Q#߯%%}RUf/lTɓs~9 ^#""=Lߣp&nj Zjf>W)(C.(7ڮY`ݟHKaH~}#n(o;KJY̅+Fؤ"" +LBAliKM|o]K ,Bʭ/H eHF^e8IfqEAx;/(5L 3^+,/^^!""Ұ„(\ef) ![YYh8ƪY3 qEZ&ǩ1ڨSjpWEM*g{o' [cQ"lC{d՟?1WbBw%#aM* PJCGz""9L(6e`s ((TX/ oMM^+V. |UuȄ%Bb$P{H6/=kN>j4 KVVZ& _v;ܬܴprjlB )"f!aD5=0Ue 6 ؼ4 _]Y)iڀX=C]S$?~~[RHOMKDDV`I~Z `br&M-mjr̐~B]Mr,ҕ&Kk2vB?E w]~xX_Zc2TҥkשQ8,+yRΒ~6$Go;!""r "",h+j@^Q,K+VJ)8QzuV)*B<ѵk!a?Ҽ煢bO r5-aRZ2d ~Foѥk6a; e5KJms!+ B)5KFm͒Ғ6@*N$Pb2 2 _U(@vϚ l, I!jU)w᭰ٶGRVՕ0*erbuEAlLU ѤJ#:7OWL)JS ,((LlC@0p~h|8 ě'jn7OE@X`qf+=؜nǾc9΍şp @3R-33c(EHXԟ>,R1qmTSPj񮭦͍[Qg6Ƽ3y,ٶ ~Eƫ>aCJHdϊoJ%8"e'<@&7Od 7ORq>Y mPؼXV=_?ٚWӰ`/6Y 5.2F) Lǩ/RQoyڦ>]b7Dxm4g@ka|<|acłc@L6Ȯ7}$DbihYplFE톾WFzrC8}BY$mӘ%)_)ΨDNnX%,IV9ulR2c-4.ǩ8cҡc7}ޅ9X2TE>pݰJ+Nl{!/߉~;maCo3FYaMyEI{^D&>fFdWIBULkVMt9N؄GvaRTS#SS؅\v =>]_ L^Cn7+o%B-&ޱ1|jcS1R4&w?~x_ث,y"-ϵoTtNwr+MbI4扂k +}FJ3[[Ljk{#w$1O׳w|7_QT>1JrzyyݝHӷi [_i r1~1z @*dIW*a udԀWm^,+>ḵY%>xaI^Y[]8 "MZ|{Ǭ),ǯJc4VtJGBTm9ʦ\ S79N`ޤ7R: N_ *8S؅o|JȫlIo:eE킫 ,pƧvT c߳-g}tէaDv OH"z|ݽ0D4t6|NnA1pd7%ngP)p-ې%aR4WX]ľ G?t |=_0ecn ~cTuQDza tdJ|xeB.3x ԼIoⱿ,D}cՅ I g1y[UzYZ E˻8Qr/=Z#2Qz M%+/xj11fF_i_*~z [VaG_; S9FM 9ߌGߞUT}vE5!x}ԟkw2qOrɆW~Yq?mx.5G-Ei*}{Z${ͨ?Z=J[@h&[cT/*=?x/;*`Z4ڕEƑ;ם߶oȺrM؄;Zi3&5dc^{c޻8wp$hl]mXT+7턙,P,w1[8{+(ɸ P7,;itXM4".nsQ<(9Go~;7 ~%õ_m~Qrglƶ; f`maƣ]23,8~'ޯ ?"#+$=ɚ hnl5|UⅢx]*/Юxq'\[[%>}(CT}v.xSqvRoTXmop{VPqZm!t7^qAwn۝dJJ2&nxFzJ<3)8a 4(ʳӉ*]K IDAT]!V1̹0hQ}>I$ӛOh!G#0ގMaTWň}aJwvtxS4:Latȸ^虊)߻cg;j6OBg/+C^eaP~Z/>8i8`Iϰ!Xdqڰ1P1Jpi\ ̅1spmZm/ƦONgE;ŋemvϽ]z [a󵇾iL6ҳ)wTw[w!TdߓX ݨU5m/;}?;̤/;Oa!"o8N(^<-KxrZIi7O 8Wulv#;?7.u[VgXpѱz 8sZU]Z.YQ1W\'hi  8^U'S%y%X}a=7}X>)=ոuE.'W?"TFoE[VtJCs_Q#ೲ$4ır@|)oe_o>'v~?{Ws%'y"\upeMrkli\ج{1ӳ='heg`긧*6Cgq? wwJ '] pMШp\z#\l G|i۶ɈbWԄr埻?@C}pq rǨhlL>lGw +8i s8ݘ6Pn 5(<<=(_ l1m؃9DN,wϘ}9Ֆr5p g VDPIkAONS1t|/Syg5s+6ĩ/+d nXjQУ_'tB!88 BmpY=NiùqaZu~a uO׳}KJ&}o$v_~fߙAcv=z}WAd is.+Щn鏻3rw=Иg uW"hԀCιv^6wųp8NCr?b4o.Jh7O7O%Z2 x\s;a4Է!x/7t]4gX ꎑhONpf&FRB=/*pˏF8򟬹O U-M1 W uL!YG~j$(<=eXN1l(|HD<4u흆 g5X tv [)JSVwX0&gw,/zc`0pKj`5{=UPm4-ƒ l-x>DrWxLz 8t0q\G$|IA)F\? GEE鍆A=iruOǶ>]pcq ~0Lj-K| rXuXDL;c{e5*pߪ XDapgxgb ]w벛v,Xz%ngh"Nu[Ip&oՑ`ܺhE_VzOsm&oByzeC|:K!5-Fybo̳눍yE=kSCYbIym*r e[Z0qrWxtI_#C$~o-GO1(򴛧@o"Ri_r7ZńWun1| ,=t" V/ V/(#}NS}+JaDύ$=歸+lwYJ]3)Iho1Z˖u0rJ_Vy02OϕPiM4-|WFoCaܤ)b&DD &ᮥ{8 [qd9kx GUbˇ'yv<P(9F=代'iN(00vdTA'`zEz8}O2\m/Z:0Â(B/X ;r5&D7O $<(lx=15{bZ08DŽOޭ2*Xz%|z5]30M|<)lY[}}xm؞>{L)¨!9$ڝae &ᔐ47OJDDq\m#ֵ J^݋|~hϦ@ q4F Օ> T|59{bÄ(uț'"""vqY}gԾl&ov ՘^F<)0J P7IDD&][pW){u(GXYaB1Q\1D {u((&DDDDDԡQXaBDDDDD{Qqk"g5Ioaٸ7pd﹀_(|DDDD+Lܰ„( Wvn9v'jS""""'aBD Gv:vM>XX|DDDD&0'""J u4> u[UzYZ E˻rɊ 7rɆW~Y}}g؞aMg.a1pd7}~z [Vyk/Ǩ}ۂ9;JG+ϏÏ' HNSй[\;E[㮇F!9UHoJ>|z5[}r`{{ {q 0N(q„3X`;Dњ o)4)ņb”>q%{=^;4~3 w/ieJ'ػCNЮӘ3*oŝ[0sp\8 simaG;pwb69Y}'/ǼۃLI,q8u8`O3۹ehz-hXaBacŲ!z!g*|r^]4^e%-bCI^\6$wj,vHc:GL`7avPGq>sP8 ʘϧZ|wy>e?ĦaaT.=1܏>}uز%M_\m#T8ԟnžu1usg&̤ i<5?Q_bE0!"zdgC.薕>6mFrE/Lt$  eъV=mk'G.RHw*s#XwqB@Qn[|h>`˺*\ؙ8Wۈ2qüG3m߲ y7ewxqq>y <`ObBSds8Ysu:zl}ȸ^Hϰ+բ`}0ۗE,C2+LI@]#|>)|#'efffuQh\GYa΂ggf켙=^Lɍ'6ĩ/aD|¥Gv:|ز ^> k o~>\6$sL^-8KG`8=3b߱|M0-րc-[!놞ku4ägnf켙?~+P߂ˆd܀Օ!=-:@LD|(@@?_8uJ]B| 4w3,8^U s"j@ q8y|L@1y>`//^>;Mk˽x.| ;_=mlVv>4<֋>qϷ-oRk_`.AsQfNYL\O^7\ ͤ weI37o<L@2=XG|cgs\~rp@ݻo߲qE-|TnYpoL̞Px!=k[c]p׃벛1rr-6|Iɢb&̤ 4pby~i-ַ]Gߞl}ruO47Z/+ [|mGZk /=VaX26X-6~{k[ ~cB@,5 o :rWng(`VϥgG _g;?qX ~:Qs8$':y3iJa!@>{]4}}'[)}=ίzljl!W'L= |;Ѧ+>&g^|g;s|Ɗ /Xj_6o&m[ݯWZz>ނ|Z ~ңز Ka!hւo4|FaZh&m0N |"oQ0igg5gSkmY(ڣy>B|ᢍ4w8ݯs 8/xAGM*_N2:p<~P~,k\ӱOnXܷz;gb ]ҫ e7M UU腉qmo{; ]fG+q=Cp{/7 >^.J`^R3 |I8 f83bXXG[(-<|pi@nI(^G({İ@ƙ;o&m8XGw#;_Wun1|M3[0[`vJ33 S |hco[|k|@sP~,4|-㵷PoiNg<ũ7qfm3iv>D c".83DD̵LZaBx7_}R^-v3Q>80߀Bm)Q8qJDDDq=L_6DJ8 ߙτDH s_Vg?k!΂g_0F))Zͤ%"40!JLWDxz"PߘvL}89ġN+t=z0F)"DDDÄ(Hݿ/ӑiTǓoØFP@jGy(`/+X g!ނ[&aS31{+V%|+} MWe>ϫ;zix%Fcs羬@ɏעE8[g3Km Zlj{Q2-Bq*YWBDDXaB&@ g0yV\1avq|w7!p)}E7ž@Sý _`R$S"""s0!J<ڄ֕غ3^"=sw"4}ӇoľMG3oP z59L0F)q+L@`+/@ d>B s9A_mgaԨD{/g(8%"" Ĥ/, mkgi mu %B[a/6h^1J8ODDDq&DK'-pF;g{{'u.ѿ1J8S"""30!J\|vVWoQDIo7*WqDS5&0F)\㊈(„( Z˽6?`=yk/Ѫh}+K4Q p)㉈(„{+V8mksMAvBl,jg;(C㔈 +Ll׷k-B6~N#,oF}aҨ0׽H!L2I/qJDDDq&D" BV1"`/ƕ%ST{+k=@u\ T1GS"""0!"=°k x5ebr26@UH Y8W-6qB?]ڸ=4dTm3Ow|ŨJ:}&6x/3 GQ` qRN@Qt$YL):EHkzjr|dU+PD^i9LJk*EۿDDDXaBDވWkdI +Y_RTqKZ;7=g^*GўGUȣoLNJR&Z!T!+B8'Tjnu jzc:4 W{Vg&)_"O&ڊHV!a66e#Jt V)`f*v 5pNĪ \+M,]fI(B{B $#-ILT/sƲ] ݠ D IDDD-(ߜ,IoT@"]iE&tOjB'J+,$N *VtRZ-zXYm*߻Y=U @2jC"N`p *K( Zĭ2 '*M[~ d5.KP;>c"T!sk Ѷ iJ+VVPiJ+RV4ڒhM)MXUY_ZþokC\+`5ߨ'=G@=5#=4_cGO(CDDDÄP]N+K,†nmIXYBF`yd4ձ=%Y\l H&[4'u@0!"tjM}Y_LSZ5KAj :-m$û,{u]i2z/$Oi۴KV@ĥ*~fm=<+M|UQb Q;9a@j;-謶@AKWZ5;}j@'p[B=(:6V%g„(q vR {bm4 =(\"+LWRzAdV'Im( >?~8+LH '[${BQ` Qz'HKVyD()?=Upµ@ꭗ {BQDD'V%z;+IJXL4+JsEX`ϋ {ߜ=(ROT*Ldl(e `rTrHYi+>[92䁈A&۵'ij+K1Tg b*K\-$!gO(0"""8 T T8c8+G0\Ƕ2RDTEQa"v'2%YX-*zw2uE EPDDD"^aV9Vq}?[ `@xYD*hV$@qIVY)p,10$Uh"LO("""_0 XO2aPbD ,y9 k' DԿYQ{*G {BQ$ EDDD+&*J1nWx#<2! o„"O?!=`/xZ0q/*ӧ'ES=(E|< ܟ*0P{B(E+V&'NpV$ZAT TENvaO(PzBQ|h,Cr\7>9I^#ޝIUXE$FEQdğF43MhWD8: ~E1 !m7(r~>]nݪꭚvխs[8Ts|\E#"9L*Lt 60'ȕ uDDWra&~c-P%~IcWN3Ί6"";kMdK+Z %"""}[I`Xsn׶}P5Dt/DA;B~䁉ۻ戱\g r}""+E :5rPJDDD&4/027k_P]2=X-pk0*HXbWEDD&XjUXm,y-X.j'""NDܨJDDE_0:F[a 0XF*w1\BP]U"""}ly DDD L o-Vˍ{ -*"""Rb MDDD LԲv.+"""""""%Ll^CDDDDDDD,<0&2.T:&Xoa-0MZ+vH1=NZ\{ŽtӬ5nQLkrXDJv;n'o~| |$Tz>k +fae Wóz:=Q `x ]ǴE%S_ GjNDDDDD3z<0105q𬭋~896.4uHo NX+ì[[Eb ?:&@ډHxV}a~r94}R_9*8ٯ%S?2Ï/.$"""""Wd=r4X`9"\%b` IƑmU87 L@DzpW봄K q~ =|}fbZ_)Wd^0UbmA}? \8;74I@Ǯspupaɼg`။2Ǯ; ]: xsvu𣃴I_j=v]nRTuѭn\%VPGH_VeWPecjY nXȹ>kyߜ5OD9ށ):| hsd^?[_yovsoGԱ =DrǮf^3 YRL`?Is#0pKw;9O [7[]{:}DDDD&E"FD&)0j`q.[J0e/5R[B19z;0""{=˅$Wޫ+M0j8\z,7ripTh$JDyCz_ĵk "Ïu0a@0̅V}v _2>_8;{YU~;Sj`sSKApѝ OFÚc`w9`H_\~SNυ4_fGH_VXz>= -w%[ [n#"SE=5}*{0?kt؞hx8yKλ%O""""}UIVY8y ]mz,u ^նk8'FHY\~ovnBS)xc~QXXf [:k 8zXrc`TtlӭQ01ገf5Upoug@Zߢ4f$SmHC?葾;1Ҷ޻'wQZfа[9jq~I-s--.Q&Bã005I"[G:;<>vN2#yx-`py}ccJ80嗿; V@[ ^FUHo ֛ʾt1<^}DDDDb k S> MhʍvX}``5-ƹgs3㋃-lL`f>SoWڣ=`ٵўj=mnO a6GDDD S [$tf5*$Hyx0l,Mn /4$"[ CoM ? (~~`k'^G-:{XE~r94 L>'eTcJZ]Ll-})yJiʣp<""""EZ)ma5<`P8:Xb;  >}\>ܾ =[p#0萢%rNfYyz#8s_;ܕ}̒?O_!""""~%]1̶u` W90ghkP{)3- 8cG)ؿj}PQ*x9|{"V=9v_bP^`fd ~pA*$-.+x^UItij^<>4غ g3kfqw VA5xixq bzױ߁sY;nC,7xgU%d n p&cG'oFSʟgz^߅ýL]+ IDATca""""[J[xkeaz⯀;6/˅ŚHyT] ^ Vk08x g^{7soügǷ 3ym6pO9y^ZO5w:f$z|g>{Fu7 6霿on p_aN]n)UuC~Wy껹}@gUB}4ٟ7`t iV_2e+""""[Rɧ3L>%"ՙ3Gj_ÁB1g͈B볧:(TUŽ_uL~g nZ!yZz]peAAdM{C3a/SIwpPnd*nnnL}v50ev6"?]qo0 R,_=<>w#&wysykTPw)& ~($_VB-~?3NQUB]wZpg nK*t)#"[̢w`.|QpO?/^CwkpA0VΪꌞ,,z'{U?8w 97և??=biX\%T>[* ~%ex8yJs3yQ""""=ClUpw_ }F nڞAWWTS%"󞁳1f$\x|[A(25Iܔ A˴?ŒWUWRt%d*¶d%T{njJ()G LDde? n:uBfU%pHpٿh)0:~0{){}KR:&8:ͱy~檽Vy$c*f[("J\kj9ߚl>~߁{C>q塋su*|?|jBKt%So!͏/6Gý ѝ\%iE*e$ WB~P%.& ~S67E r JyԿ쯪 ]ZJo,:e/8I=sSXo_0` 5zL%㋂C ^̴)] L fE= Lhy 񾇈H_0fdPy2`-;=h'?KnFJ*DDD?j`"""ğv{0'Κ RT %"""})#n g7v~m`A3#)9W )zٯ$2t sUBHo 2Sl"FP"""_(0qP7n~^~'pøQCDDDu)0/Xn(Ԧ5Q%u] Lbjcj]urE_EDDDDDDDB(0 Q`""""""""ҥE_mӁ ڕ@՝rDDDDDDD470XngZXDDDDDDDJDSrDDDDDDDDBb D$Z)TۨHo %"gY VJz~Rc-K˃X+v xuTk i`9c9>VDӒiue&Rji/ iN`Xb @P7SR q C4 Rj7.DDDIZsKqX؈X-#:Np Y[$D)UH6^JBlk%ԮWX*,c ይH5THŃ'DLnR)9 .֫ KژZL-b9m l5H fђ.G-6x5-zzNLׅ"ՙJ(lnU-)p{YljXHP%%-Y9c~mMQK-m{{98au^J9 L{ѾBvP&;0f>U %=SPX/0@TJ/f$iZlf/BT %}II[0`XӼ~v-kIHwKYӶ.Ds6qnb tS*gu*ɶ&]MqPIӦP窄J(rRa}MdHDڧɶU4HAM^Z}h FB[qЙJ(7@Բ*L>bTJzB'*DVqְ_Dbc`T"Rod-ISDʚ@nZIu M:yM*e_tG:Q lk]vl]D1dZ$s5XvJzB*Jy*u`jxim#:Ut';777܎JE~+HAvEI*eU4{/UBI)u{~9+>R8ekOҩ p_ JzX*@&V&km(pO~oXiT/ϦvU&)khHW9LZl<*ydNS@r*L?OPRj] &ۦ8jDJe^ugIg.؋ K2?i[ PP:X eH9+i`*d,u\`cjvAKp[(@[H^m'V^ڲvf{{!Ut5lJfu.%2ك$ h>-+}T %J(K {rgDJU^Z;.]ǪJzL+DT˜|/aK0Եn :%r{D_XqMӥM<^K' ]V|4:$P -*:] eLlXkanXk,ڊHQY'Ywj} SLڶ[mNUYZS)(e ؘ >S)| nI,"sR䂒fuk&z XP%tnŵ{Ue"fsaI찤@ԝ仾4Q%tTBe*M~U Ɓ[4zݯᆑގ; MR%1ZcSd-kazo˅],_AIΝvDKf?4Ï~tOlHְ9UIư $ltE[_hHWX}dzZ.oa}r+KUa?uNT %UJ(y6rQ7xj^t֊Fs_]ET@\?uJ().TB"eg]AɥڊHOSi/{>siSG }[sU¤{>}d]D hL~]R_au-bRmRTRTū~%ac$ltJkTngiD*릳q`#h/_ Ϻ>p_uѠjӮgȈjO8pn)K LD/w"~YC~Ӑ\'oƦ 3d# 0#rUmSV}h?{+Wobѧ Ӈ$J KK B+p%{/kG-T|~Tͽ~mAbzĨL`S]U %CP䉫3t۟Ӕ1>+׊Fssݑe=KWvET:;>SU %ݦ+DZ&qLk 3й-Z.(LD?*o[ ɷ2U$_ւ(཮J() *2yy.9C8=oFkFHEsӇd/º9DPAbOTB;{$;* ܙ#cϱ +>ͼESXvLfg*7>TYfW>+U %QJ(.33-Mҭk~?($/? 7uHka>_P@ԕP7ҝvޥ >ܼ|izqmb+WB㦵K,> #~ v-fϱ:KoRw`xݯ6X̿’d&v(a%w&)DDIT`mT"mJ&7;c\-%䯄R`7E&*9صLX⸾>Fv&xcXv =4|=-n[Gb>QKh4T>ven\&zX_TXqA&QJ()*@̕>0UV`b Ei(p@p I:tlA=.oͿspQ۹ܲ'W?O2jnYHSO1nR^Z#"5|,&3=JWOU %ʤJl40ujr\c9Բ603oT <'p+DD)ؓ6?> w { WPז%a>g9$Rrx6~u͇1U;U+ ŨJlJ(UH+m`:8Uc9y’wޙmنޚ:f/16n͛ٸq#| .---yR+f{RZ Nct,_-x/bK@g6_ى XUGP5h[wI%ha?d*^ 6~@v? ׀ׂÒuT OJ(PYA2_aw3Aq;ڢh?1 </vۍ~׻&2됄~PXR(慿F}_]aÆ ע*TArLEAuW//V j=A‹f&|vC*P"eI-+4ʠe]CDC@’|}[{ð&Oټy3'QP[%`/_gMł\3;|¡?*go)XqSzoQpX?$J+,Q%TWޕP"eI۞TDJ%;sÒB;ڋgyL9dixk KT %λ!+ DŽu M-÷nHӡUD&-s޷X,?VP}\*D&r5l2U&mZV:NDJf_`хډtPб;0 ?xO’{:u!s qa^nRI0Ȩ$ʖ5 @ &ܠ߻+¡I}TP}OgYJԲq lǛZ^-vtow. >Oݠ ET^Gu"K~B+.q[8,¤QP3Я8_:{\:mJ(- ekwΙt=s5`E^+T"ae+#4)5dDqDN{" 2W@c~'}?hkp ߀*&?, eߖbJ:[, M趐cؼ,k&""eLXUma=B{iƷWmx *H[DW{ƒtľpHґ>YL,*0)J(- 0bDGdEʁ%k-PjW=wtžw $$jLO~LJ_Q M$/ܗ+ҫ<0("TQQYiH$?.ڶ?}ه38h{R)֭[… Y`Ac}> OE{n0Yၤ?pu^^_x龦Ck{>YHOP}GTpOʡJG<0!4 GD*++΅^XiL4ΠAܭG;,sqDZ{s饗;*t$SO=I&tҬ]a? ڣX`꩘``jksG*O#!J-fcC}禿4=t4_GvuQUP]"=+I~>')evCG)ִjv}wRd#F0eʔvoԀsg瞜tIuPp+,Fs=3_2`Z pc̰̫&Q88Y4B3`@7?t /u/:O=W/֯~%T}>)_6r)&r;DvT_DS IDAT7',Gu/B+q衇2tP.]ʆ 4io(WnI&ub 8;=lJ=@_=33cպ } ePs)e7ƫ4r@tpbCWLxLq/]PPS/Z4eͺD"Saço%Hɔ>0ev6}զƁ PH}8X~gewgСy䑝ਨ`„ R)͛ǧ~ĉqvmR֐"FK:ֶ 3[ ȯΟ{h6n[m]oV78=>}R`sP1-YE+H?tYk(J(Ii{=VډtƖjֳT"_Bo~i577s70yd&NXhcƌ_26l`ҥZ>Jz_T#~Xb %^& %4i*MZaIe&bP,lSxfm̕=yXE׻*9 X`/T5]Ya6%abzV4lL+xC5W۪2(v.W;6Pt_z@cw.hQ8W^z饶*;KvC rgty*6lCuu5D+Wk.n.0<3o:j'fag$ILx$5$ S-ᄚfkK nl9\ w *yPƪQP%T2ū2)GLS8w=qɭEYRI[ljXH䋡'Mmp?M6-nD" /ж7`ӦM >vۭ;qFq;8 TFyK*M[I4Ȕ(ʣfĒm~1#oT]m- sbX hbf *ԏp%Vl[ĐxK[`ߝ1Y*N$(0>4\v|ޤ )P&<~uv… `=mݶ{CZzx*R.0̊{` WѦ,}Ұ9+֎%SO9H1dZ I/d2ɭvտqq<̂mov(HR[;_Xs.L`(zcb|@,DʤOE5%;(+4yd`,U"R̠X8iH^E 9}& k!%Qh"t>6:w/YŎMYvL$J ;(w~c<| \xĉ.9Q__1{챜y KG-01U&tp3)@2s*z&-cBSDګ&bVUi*n+ 'uMDzVH )M:oj: RW_}u޻m.$R\`>gy!  8H`HEFmI#73+ND"߇/D_3x͒.4i[ؔ``j$"N0~ ݓW\H_^}Q_&V'Ofvl{ݰaK.lj*>8Ov"ݥ#Gf͚"=m  sƞ%ϏZ)!?˱=LjZ7*g0 3mPձ*K[Ҏ_2ǜ{|VB*5xDʒgߞ'^ይx}.Ŏ7[o=!I*[^[o{M2|:tpk|EZgko`(3k5c(n:PP)X*떮S;AלJY pW[MD:-X8裱UGM rXuiJ@}6r;D6P_0 [D-gtG_; 䋏cw|aIL СޝZL8T*K/qt67gС^U$Juu5^{-+V`Μ9,_’SN9j/BwC J1Օ{ɀxR#)A$%i;1VAw~youi>R_RO17j؁]5$^#sn.&Z 1.z;ꙫW^p^:_{"%Ox'@Rr'O~uIL[^?裂qfz-&MľDlҤITWW{{漞Jk;}{BtegQcpˆNIaX kc| m*cbm%n@ISKќ0'>DZ H瞷-CۛO*sNɧ+_a+ ^J;qSr$w^fsrs׷=֕cowaʔ)1z+3 )_8ck*LRFciZG\9 )-d&0Ƙ{DJi`,|iL)tnJ==>BIiӿ_~߾p&Mh GxcE zݯᆑގ;;'_("ehs=s=ŚHV(H{\ǫ^ID0$nlxkD9g谘ILqQ;)l۟T|[ϓ \ʜHYӢISK8L}V|4կ-ĕ嶐[+""%(0X¯kؚg% j&aCc2iIJΟ8iԂr)W[Q &XWqcY l34}ǀ7O=gT<v[m`Q H؆/#I+K4nZ˂[Y%.0&""+HOysi* 2Ih Xl7wLKTmƌ4 LOtq;z40uX81dxS'֦08q<8GW}-sss Y ǺljH%װ/&.(i&14OTDD(8[ۡ C0hlyDZхGMe-I<}Y%nsI%GD$L!1UH ѡI۔l[TzTadT*)Gt[= J\p<g'^'&/8ZkHA+>^uY|o֭KZNj5tBP${LS%C3q@يLf/)3%Ll’.[ìb@n $Ǔ'N}4o%բ~љi8K#/$;,q%Mj#-{7"Rj---RmKQ,I{H1mZkGzJJ(ژ穔&Yɮ2+[DŽbDv`;<#I4 Z$^_K~dG~ fD+Caf4n~XW<HmڴF+ؓ`OdKsad?7(}7UWO,,ei~Sk8N: |P4ҝvޥ >P9ru~h[qY㦵!G=]DITHDʁQ'RJD͙_,,Z.H3a'”9/-iB_aٓ+Y匟'i 57̾KJxq#VRUt^g1 a^5߉_LO?f"u |o-_7`OJ B)'~(~Dʅ*ԁڜ=6d/|LIL&p g޸UKg !mE:,OX( Au៻{,D@7{""_hC(ȓ^ec%[BT)_,-8[*VXTQ{lKUQ5pUEzT O` d N w- %B7XD^3ۏ ӯ]P} &i2 us'"")(^ [*>˜| ˘b&5N@uWOw%!skh}> ;·v%.H:!?x]  @%A*~t3pn`OJ+y %Lk,YkLK1-VGJ1ͯP:e. 57nqQATn?G%.0~UIF]_8,.0qQߒB}Eݝ]\_6s{Rj DDD+y`bjh85Pcuoj[tcg,Ǜd{ lį*j*qզAreC_ӡ͝G&QW: U-$~BoI{}= L@`OJ+*LDDD.&xw)r. @%r;d5 S\XN`QIf0o%KaYԼK'N }I:}_͏brԷp_w2b XJg>ٿ)8.H_ҫN`@Upe m~c侣A?oK@Wƒ|a`8,q(ؓR?}X/$ο۬pbGN|&},.8)IMmwUxC%=B^m=)>Z,؃>ϢU{0~,V"ОʦI_ FDT@g㣬& aĈ Bk-V n߶bov[cU\U[dBmA[{'X]ښZ7Š 7j@!Lqrf\&L2$q=2s͹̄Cr9&]#K,tw@jԯ/} y_ e?4[v HuvK3S/Mɨ`#DdW$A`{vyP^#"{@5l˙%.$6Jᴓ{=dނ^eeǹY_<\bgݔ{/ߑ~kU [#_xմ}MӿI5 Z",yРDar]w/l0 }_}}{w БI? ;'_~M:oQ"Q}6{4ujL<K9>yA08+wn۾'"Jr$lk6Rxwͯ@o~K/r)xϣ6z:{ _~jZW{WDrO#S>XDDd8;C]$Qc)O,Y>R̅f:Ʀ?4 &?`@yGr>epLM#g#LYF_l}Ogut{d~l|X3q]}H4+@ J [m,qM|`&.GY>??8p_F[hﴜpj2/"lcd4YF`i*2:tharᏞ ?kA<0/5[yϷ}%M= WDC#G54$`9&ç7pD|{-8šL;ܹd; #o8ӯYF.ete}/PɄI>} 6ДwC&]m `k\DDdiҽ:O~p_$4MLpe-f9ĀOVpG2K7 tT IDATwyhw;-'WYF#_;}~[5*R%0i|_e < n'"""C[7ۘޗ@p.dlwį"fJW8ouv TR BKMuO$ʙȑt\8ìd )(Wwǹ\EJD&l̝J ;0 E k8 0i�Ab4!X ?y\TF&GJ#Z)nMl;/%Ί;nCwMFeNoYFtI1vy#"C&K;'c$+kLLÐ#KeeSc͸>ٿL DLaפxy_bn4|ei:Hd(hƲ].fyN8@ F=\=:?^O_XJ'L-p٬ぞ!YF[vKg_>f\ߟFD &(Ld3M雅[iRNd`[4Ms7%5ԉU:4k(6? UNJ=~Om^3i2zejS닩ML&Ox-1+،-"O{, U""" |_1 V))ޖ{I`w_%fsKa1[_ 4 |J,=)5ebeܭjSዷkq RseˀPDDD$.>sϓgn)@ 3la,eWâϛL\?ܙqfşQDH Ԧ!"""9qx*yv([*d {=dٔlJᶯ˛#gU|mT2L  E"`bh՜KJ*eHhJ8U T"@"MC¯ />Xqe$" .uw|8տ8| ^n6-,,#e ' wtuoݛ4QHP2La""""2cϱ-:&"""Ô&'|QA"""g>ٝ0ARg;-GDDD8M\'O2ki7`)9"""À2LɸDDD$|0q丅_EDDdSd0S0x)&ݛ]RPSrBp0bxvsɶ>ѷc#{#""&^hMqw&EODDDd@G c0H>[4Qdpt(`"""28|L LDL(9 Ƞ(r!,:=)#""0lC&X""""QDDDDğ w+(;o@*}6xgTS1֞y1|h<‡J/ASٹJl}插9㋩6q8Lޏ䟦䈈E&,p`;XW-+=X }_?}F_un/} nG<̺^ u´/vm]^0 }0y%"""=~b񷼳N:Xv*|Ȗ=YIsw_;&go1o&{װp= uo5t Zg&9c7qt0{kXbKCP %R0IFGAKAÝ0ľI;` p(?M}sN™S>z.,y 6[_k۾8/z~b߿>9d&8h݁h~}nx$s#G|f|{.yO|>\*6hgi3f]u y%fB-^l<9%JN,y1M;?O# ;b2B>/?X <2ܞS\ Dر'ysNO^0.?}?ڲ,{ZL(eB0NAOJG%~/މ>5y`CLKo{ɟ}2sg%6'B3 ֟*x ʄR&ȑFp^e85}ہ, 6}FNyYap{'7u"lyv |Kʄ]ҥL(Lo4S+ NL&~f柘ɱGWФ/ _P"M#XgV*Jᴓt^;` fu6n\v?MdO-|~LGI!R&VI""2nlVV2z S0T6kojwlipnFjkh`Hr-@LMηk ­'";ko`[QzK͂; f6opd| SL#_fBmޞXfdm"~fB}z:kg'2=~/LDd3mj͞5}:eUUt \l GM)˗ǟ75*`rřum"Z֬I=_˚5@Hۉ Ol,6tr}&㡧`Hܹ](9씅^#jiͼ.%=>Mẗ́J5L(e3JG|-L[j2NfBy-|9yFҔP55yo}E n퇢"[Yu6Pթ]ckKFҩZ%L x[Ԃ6D_LKLzgwpW a~s>s3T}gt΄D&`fBe[kL(RDDLyM VO7ؾd /̜ɓϝqy2%Kk ڛҜyh( ) J\MGԩ<$m&NdSuuRVʤ9(eWZ*KG.17 |:|EO.z xVN^R6n /%vK삥?7ǔoo3?ˏ|>[(BfBIj3:L&OxmDv@fBF^lwb7~¼7pL(Ҕc**8mPs3KYG#J-kŊA&55,˖j:\/qg3i.X)˗S 2a\^^RlizyA|N?ݗU]) Rؾ.s̖ ﱙ>r 8RR }l~~{[}~?6cat0뺞3z씎wyM:꩗L拷u_:60 سv-{֮MrmommR*uc!蕝k>fL(ʄ)l05}:̉?sժaM&.{kk St&^}u}u'wZ&M0'=mj%z3|Gyf gʄ2 %rdRDD. fJ%KҴ&̝}}jn&8e c++5}zk|gЈ wKCŗS]\ Rؔ %2x4%GD\hkhҴ>tΖi26x%~Dd(.V.fȶ[U?l&ԡɄڴ5ށUwS&S]pʔ\gH c++Y8i7-kpŀ)@}ɒ^t fڴ)MK#OI1D 2D 2LD$FMܱ}{Ó9IСfZ:'0"4EDD$ה %Rؔa""yLBͩ´իQӵzuۗ,[z宀s7h+؜p쭭Ѧϙi+W[>K&?eB.LD$­n 1 3q땸]n=ꍜq=Lnme˕W"""""" HޅI{x6Zߺv+ī  gZ6Akjqm]=kײu0"""""É&"wMMȓO:;{kk}LxQD5\/)S W\U@l{S]woz|LD$ pL) '錚>YF;w`_VUŘ Bbuh(1&#NixY#"""""}ݾ9Ș 6p6nxcom-K&60a\^)7"""""E%KTTp 7i=H:W[CMM@r"""""{ ȀU=er^v`7̉ls$*)+c9to G$Ž䞦Ȁٺ`)SSskj7{v|^=2Mr֯Ϫi-|-e-3(;{֮eҥ3jMDDDDDr':UwBͼ0s&VO')-/*BNy{ݺ4-{ 57Z__Q+`ۢEAP$8K\$'"@&""#yV=,'*2EC!6ϛǫ\CgKKY8cKTT0}Z-ȝS^SVc}EXɿb^D(k2T=ä[9psnv̈Ujc++7{6G>ڽ75eT9t:/]rz=7\Od(bXL$ߢN(#L{ôXd0p{XGWrD2zX!A"24540|~t; h}-DhJJ&2~v6~И؋:%;P1$#Pާju&EDDDŀvGgt0/d};yk ,{ PO Ҁu@t`znK!+D^ 9P%9`b.vc±Bgލu^NK<('k &xNbEDD}~QGK${U;ɵH,芵z"U`O]QK34IT`&p;Д!aC̯L C!>>p2\e{q:I >[| ͑hEwtؓ;$z'o$@L U1ksU`e:Ab}5X0e"rm>Fx`/>*yt_vG?@WSop/9D@ HU հ >bpfو PjFáؽvǡ$~5;?zgiji=WG8xHD ˱FRgA9=eHBP[WVӘHADb>]nL"ĀA:cIKX} 4͛aήX;07CݏY&Q pExۄE_u#M`o$c; EF8Vāp"Ⱥe `IYg2”I=,c`M LEDDDP(uw:_+%% tƊ ddQQEaN$. p8XaL͒ߵ; t, {on Q O|fm#uN]%Łc;(V NI!FHZddUv>N`IgA(H z,YyU0 )X""""*iZwS<uWCsx;۸#ZH1YvbEGK}3N/.헏G3$lv,._O.fןX5+ݯ-f_8Ȉ-ᝮI+?/`_̈́}Zc)xy0酦6%"Gc** / 3g2666)*ʪh>m{Θ ϙZZ^۶Q r}vCDB"Q]@9pB]9b?.qEك"MJ%/B cսѝX}{fo>NЮX\P'6>ܝR2(ˆ@ʊv±"7Fw޸}D2f{Z;5V6wUCC"/?޹lYqpʔk-k 뀉3~SQןkgK oZI7 kjhY&a"PØtuW\w۵%\tn%'M~'RO$B׫;7DR6[o$MqpL$Y3Ϛ^|aɬx=(+"'K< ,\3j;SD `Ll׵{8gдs2N?`KgZ!A޽A!~%.7hѣ9~Qa+־w?; 󳮍E"@j ;~{-S oK+Pq1#Nҩްƞؖ?o>o:F'8fn`D @Ll׻,H޸w/Yȑ2)-/ٵbED7h9t[ ?s=bJH ^h4)"C7#^E3ݩ nTKhW| 9[_wӊ?t(N(;#eȰNc{9*qh;=xul`o'8)py1ՠ"yVZ^. o Gy^nW_w ='nC;ȰA 4*܀FPw\B IDATqgM(qGǀ1Fس}R}shKsܺ%VƉ~#GLLmz*jXp3a㽵 CR[CfS0jtTTp1a"gvo]$6h@ҡPoB>wW`QHAt=oT8j*q01aүo 1<'\qE֥i)rd۳v-o0^) @5pp>PHJM <j3z S3Z_O[CCnJSQA4!3+!;6ble%g -[hkhSDZ=c.O}UZ^ΨWxgKKV}"8e )S57jnqgu3 3{1\88{x *, X=ҙT3&^/pB"GT-̒`"5z{SSV`3xqg'/=_[2(ŋW˞`}TZ^Τn|8ybvXEz*)+c:.ָٳ9ybBͼ|e)mLE3xkxs*N[OjײfM&55|9%e׺MMlʤ`2m~ ?jn晩S3,_T]0q3JFMNIYY"9p%Ct"u*XN"HSLL`O7!%WFػ,XWo`O T,c0^FwjDr%Y*`03xfԔuuHd7J6UW86ijLQӧSZ^NQ0ocv\_hI0NBQ0Hpfe}*-/gҥ=%}*eߟ >7l`g 85mZT66JSPVUߡ<ڛz [Y"2X,9S98c9c cXc>#ĿLJ}uV:; | ؂ln& ;ɯ Z>_1}?!1=fG tLwTW59WzH wy&MKmojbIqgsSRVv֩Km l] )X3jt^Jʪ?g{kں`g?oZezL)ᴕ+) 9erNϙti|nmkI>2K2a(~r\'\qEj< ^gjX'/^([[ٶh-kă%eeLxvogImOD:ouWi&΍gu]m66*`"%G .` @<bƱX,i7"Ã0_njGͤI6mO?4{챇,.(Ԛ,)--M =k4?W`ox uvvgnJ(q3 TceUl H WuPޝU|TD2rܩ ll䅙3{d [KpzeUU:f6UW8O{S/_voFQ0i+Wﭭ噩SS(iY3fp 7P 29ϙDC!; 57y<­wiP:4[uQ̚5s!uw05$ H4ƏϬYzB{/wQG1j(N8M_onY xT){ZD XQ9? 2,렬dl&sd=&KDnlJ$M}NstMǯ^sMtTucSd|koZ|>ڹlY%`KVRiY&^<جLGIYYL1i@2oD@'Xrꩧf xٻ޻ޭHR{ 0Y!3fpЙ`p&02KΣfIiii`+~j;27c/}tȑTıUhL? b]i`ޤp=`3݆xSpZb0 >VmXGiyyq6pkkڌh(?{n7+~L܀QӦx=n]n#|}TWz7Ʈz㧿}"wl?tHΤ색'kQ ]w DGc&A*8~%^9i2Qbo`|L`ӑ~Z1PD UާTCXPOtH+1Ž@ύ)ehd1=Wm`/ TC307i3as."='S ܚHjojuҴN(%"Mk}=c9ͷ\~6W6ӤџLynW˚5|9E '\qER$bV6A!DdDfc_>; 3bL$g@&V,gMW1LoDz'϶NuA_75 [nI@ %ee`Iw0Y&6C%]\}6\#ym hYg錭R{T,ӭP)Ř%nW6,)%9gbR^5jPT+X"Ru}NwٹlY)!馑[[B2^8I'-_P)) 0wntȑ u>[ Ha)!u`H%R%y^W"GL1y/y1>?UdxSVҭ&+dl)4ٺ`Aʶh:l便L׶mڔed f_'̝ ⁓l\"bxbN9LMٲe K{d8;FD ဉWݛm6}C .>SD"޽{,屙PD" )CK2Lau=sLCnAD3~K%KLN^8HŮxrQ=^3}_v.[|IWQ0)˗g˧ ߉}'pCq5kR/7'e~mIIY'/^dI+]K8_EkĈ\{em̙gyf :s'rWp7y^ i,i%)@a""93 s˖qaVg1W_MYUϝqFR&͞MyMM|@_Z^9st)E Jʒ{kkٷ~=fώmozڛM]1I5JSgK ۗ,lްK6>妬W__-&̝;>ȾS~=kƋN̦+a2P`ɰ2,'=x#6M6K.Gy$SӴFəgI$!ssfu^|'r饗2k,>pUW >EVɑկ )qjonuu\qa,ƇGe9,W{SStiWǏ[r%%eelJw>~_xmIrIcLEٶhۮ+ɋ^uu`I4bYMs;̿<7lnmɦ+$m^a/@2lF_~9Ǐ,;^{7pf8*7|+Wa+C@xS$wf?NRDdoz VϙBlJ^ríl]Wkۗ,ax[֬aڵ)‚ngN[[ 3g6e+-}m ٵbE6+;yyd;K/?""C_ɰa1bԧ25O "=?0w L8ꨣ2Rg>E{}A$⁹h+}KMRvw_q^#xpM@"yTٷ~=&Nx1Ζڛ8ؘ4 ضhouc**SQAgK m IY3MMկ40yjD$m{|FRP.:(FrM `K3˃L D*íY H͹B̈́3fdS"sgћVoGaz1>ћsں3}vIYUUnk}}Vy6l̙3+زe tX9{,~>… Siz{NcȑtuucǎL͇Xl}vxL"4peojHBj'pE 556C/{emmn<^jl׮.}^z6ƍǴiwZ[[9|pN1 }4%G t 3`""ge8>E n7W8-Bd0OٳvmV"13f$HUUUQ)m[ ljӟ4_~9>lS_:,Ǝ˖-[_f̙|uĮc&nN~4Er!;ڻ|6&"CLgKK<ˤWrʪti|_"jD]hWDD ۃ>HEEr ^{-K,t%%%\zb^?0sNk L /ثac 29CcizsjޭG|6e E;-#rd)D ۶ Z;n=+E 6mЦM"2 DR {|fڴi\r%wK{-Z\\׿m;^="w'? /bׯ{vP\2(^mi;3%k]b6Ď=B-dj&2v~;۳&~-""l۶~O|駟NsaHAGy'}(g~,hz H^4"Y2f$QgSDDD,q+鈈?_ϼyR9r$_|1H˗Q2uTo;g/8OdcDzm;g$q%Q:`Rʁ%pKv""RX4FDD򭣣,\Yflglٲ%e;wk.N9>)`{1|O|Opxqi|`򫙎ZQlvFb6`b#((꠬V>KDDDDDDyٰaC6v_~95pZ[?~|ʶ+Cb;`<"}~GqH,g H$5ډwM[[kSNs!q=ô1b;L%{7uoϼGd}xjˌĎ>%n$I]"DDDDDDd@tttf۷/t_~s=7Ck%0[[^mw>DFH?$e,ɟDI]_8E_ޓZ=l&o@ 5d>Ukɫ@K+NxhB wmt%ErwJAE?=Fb>v{8  Jl~c +;l~t;ubA} v]y0 9 e 5 ,"2(:&Q-}&پǰ揟Lؿ͏ ]b3K-3%'߫ `A54x}؎at =w$_؋Hn:&Q{x% p?K~{I}Ý#Ypsjc]bL߀I^3LRh<K>ٗO@W"^DD'QO<]]b~T7ãҟAı揟dqI4SZ'Q$ tto6hT$f}͜Jd+`"""2|x (""1g3Akߡw_ yԊ?6/1IpH~\7oLIj6=E^m{sK` M5MAÌW[ɣ=wHu)NAw#3t=|}(~EظY3FW(w:(9%GvJ{+>/k3ɿG `.w1`ۺw`.Ž$p'sƄH*ش)i6`+?cL?=1otߠ|nj3ɇ۷OJr?OvﳃU;P `fB#{ǝ4K//_IɯrTie"^qMoNOW[xxяWYrumg3LL<+9fcL`R*G?|8ɑ=w ؅x{qnNEǀ؃=Xf̘ 8ػw/7of׮] Q0Xn8q"?~<"~^y^uw}vjځ+aKA͙8 1>3Y^c18̔BhEri~L_A ٷT;5/hb ɼcDŽf23p[ {_{y͏ X{",7@ c:q%YJj1/ڝOb HFbjtZzpݹHuF{^np~*}oyMox&7IdK߻mA}Py>N@I6w:]MU*V[h$ '1X~GPƉՉ Lff 'qe/R¶-L ^l%nAB潫iZLhEjg A~ꝲ;[/zpkSpRe}= >o:_2w:_ H79ylhrK?y9wUTWaօ;PDDdxid*}Lioy{nvsRܾA}ۿ(sAYL3MY huM6oI(*puDDdj!TT~ MNE Id)9^E{A^{ۉ_;h>>*V&W'ۿ׏,n1 Re\ TbKlhfmj4LN19DDD$_`Vvv[ݳ&nqL}]7_SCo0OTli_=}V _ jgy-ڭ&35X[8w'&PtTDDDx(3X`Y%ثOu"]7L&j)9jpݗٟȔw`%\$u]-׶b ߇:0p~1' ""2׾GGq+kQ01QAE"uoIȱcosM[oc i"H@r 7Anr8m"IJs"bX|ٝZ=ڝh>Zf^?a u˾FI1uº% MDDd3]772£]I)\8qIQj&b[&b&2:BIiX`/4HEDDZ\0|$j8G\qܜAI" """S˨³b&}~ `s&^0qWIjh\=D'}""2%%P$j VX"""2x}ВNJ[&I J=/[DD2 w{:|ostlZYDDDDDDD?7^[I b-ü1m>0kYlwqoܹy>:kWb8fqsrɑ/O0p0j7 m@MI#YxDDD*gឫ{?ھ|+DNj&%jLȹAU5`֨ ` SVjn+8񺹟 yy*"""ׇ(+_a %VAz׊zm$X/Ɠ K\t[an;N~@[ &~{WHDDD*IW8_ԓ ))C&gcpWsndXUdL-_8&ؗa.G.2,$)7 KDDd*vR"X#NO+E9gFÚnaDmb}')nɚG|"S0DDDL'5E9Iy9XeM.3'4&cbv XPz1k*i+0xZo~ǟ2bn@WQ|Թ̀OF`ለHq'yn:~ <0!u: &lneքgwIq=h3ß{}H3yv jQ}MVmшy5jni$"""""""wknL+h`m 9tٸ z5dm1䶌_DtЁ FcZ~DDDDDDD *wB&2ƽOī8Q H_fu枺+^m'!+""""""R:[YA>Эj T6H LD`xw9ƣMK:.)-(E{7 8B D^`#ܗ"1`qO"""""""R Dc-2X&M0`fvY0aCEDDDDDDDƭ @t1asHQu=4 5&M0ŧfH K`_C|(koMY?pЄ7 """"2KFv<z{E" bf54g(B`Y Y\e$&}̍EDDWWɑJHڸ3T;;9e H$k\kkYq]9Z}3ŸCx'2(*b"""Sk!JHT, K浶1*a9#IynkGK $[Yqs[Zv<(|Q_d_=4HER 1s mmTÜܵ+i3~mm)C!涴p{,^njJy\S]WGҥmi! Rl~^njBf-ܣJoNd6϶g8};6yf{ʙ.{d9Rf K;:]w\DDDDFOSrDDDdYq#3/75e KFb1ػ7o!G7lG47Ͳ E)ߣPccDDDd\:¤ J=|vdv-3,""r} Yu{"Le8l(9}k_3ZC(==km>{XeI7zÀ&1%"""r}jO8}7.zꩧRBrI9WZ4sC&ډq;;Vu4P4j kkinθ&%""""r dF}]?]@УL&.""r=)x`bGڈY9EXAȯ~:}vQٹ--)We82je?Kk&DJDDD$O]Amm\b__#xU,ۿ`.ЭЫd KL ϋIZMrqNq:SvP2zNέ6ê:egnkobbEDD7L vxMQH8Α$n=="v\^sLQ^ݼ9㉈:0xl;DDDD*uLEP YCd:^_p\e;w2<0\DDD򠠁I @?SsRҊ`L[اu8̨gyww6m;妦mL&&|рvǦEDDD|8ӝښS7~dl Bmi8'AE\EDDJK`/HX ۚ`[]DDDDw/{^1gƍ9VPc=ع3֬aM5-/0R bݰրgO1`k2a70b(卽{0bc޽e( 9_cMs3ڢ/,"""ckM1a%h fnNعH*a>|Y ~gg,ԚI d^k+`5%YEDDDJ_QnЁꗈH E߾~\]Wnn~qdF}=K;:an۳'lmmVӝrꩧQ*"""R >%B8kO sxJjyۚ5)uJ`?%o>GL*?zr\9~ e:@ʴDDDw_.yӯh} ,K^do+""""""" D!`#Lj3l2asD2 EDDDDDD$O_MT`2q(4)WX10ku&%V)/W+k`ȶcuCj~9C}M0y)ʁ O:^"""""""2n_bݍkۡKELހ@_[˄MV0$2Xsf |̼#,C`+0 \,Z`J-~m= zN"$T:inϸFx#U 0HnV_lLX]~&D`R 20@1DDDDDDD$! Cֵx %#XKƷ_XIt>r3aeSʪ?sZNVhRUOq 3IDDDDDD:3 کdktet%&QsZN"4 tfJ7ϋ~5Ĺ5Kq.ʽ?iZǩ)j6 O&Gk$LXs7V2 +4)? Hv&INŹ5gk&@}{Mݔ < 43` S#0qr'[@_fDhRjcHrB~",I ZH. W zq uqo7&뵏S7 8@0ckjN%V8Īka L#M 9DDDDDDDDR.&90 &_ca֓KXnXk9Rʴ$圚":& W$^~Bjt!$qЁIZaVӪF&@/D<O~k֊"""""""2v?)59%dMOeLc^ODDDDDDD$XRX\%9& L9"ClR ǚb\#C2[tHH)wQ¯I )i5P/?f]Vיr XLķ;+ֈHiT֋5:aȻ'9=*p @7VroBSGnPD#lɰdbű-SHDDDDDDD$s~Og CE La;H1` O5Yam=bG`&s4GMW7%""""""" yுO  <0V ?E &>ǯ] X |ݹy# LDDDDDDD%B>1Ҧt.B/+ VXǯu>p/.:SKDDDDDDd heBW1`_X8mDDDDDDD$jR7U[.x` չmEDDDDDD$*@PUD2[|LDDDDDDD$ޙiŨaנ@Z.)7|rI#lks2΍aDDDDDDDX*ꘜMnʁJMəXWfdj%"""""""Q(W`""""""""Lï](0I2P`2a4DDDDDDD4` 3DDDDDDD4 LDDDDDDDD\r"""""U`Fb}0U1 =xgH LDDDdn[8};m۲ ΢cMyO`m-ZB E DE"~X$0C(/.YH,淛L&""""RP%C(oݛoJMss6عvMa$ΝNe8[9en""2ATDDDDD jc8sgƀ# dzᅬa @y(Ē;XqڬmK{2p&*a=DDdhstH,qtI;}:{[b Cz/_ܖ2b/xV 6n4DDDDD fnK=tgg%%;;3<0LW`,uJNf(M~}WO?fnK=b Y.-,qE"ݰޒ2MV&Fz~삶""Rz2N{rPS 55ޯIΊ'P(矏:{4F峏$kkXEZzzAnioM IDAT.vGPIDDJ nvtEC,ۿU7#GXe,G~eV;7d)}vtm~9V&ٓte8̲aV^`ɎY3ѯ=XP l$> ~9~9V;*#GXiSwpn{߾|{| zfۿUo;xH!̅^8Z[i$?f*k/DX%""&"""?C(w8yjl$+8aExz N0Z[ﻏj=opfNCqK{Y jh`nK G֭ i:MkϦuacx`}}:Nڕ0A=CѨ 5ܶgO%uu]w=xR]WGu]s[Z8kǶlɩ ;wGr5{*~b }?&w`R e$""ŧDDDev~zzz\bD¼V ?]6GOtuqb5A8pc[pK{;`X$™.?1m 6n<%;vpt`Fn۳~|^adF}=sV:|Y^\$cN(k&ƀtZ{zž>.2pfzc4jQ_ϩ?pAM_.ܴY ^28Nӗ/`H^|1]H6Αב ?HϨ$""G˼VO#۶`FtnK 3~ ?h]2XE#mmBEδ+o~s`p['vLľ2fɎ*Y^Q`vqK{; 7mG~dQמ͌zoG7lH D kYg7Ō2 Qnޜ6:ؖ-jhH4dVC,ܴ)*1LWxaϑ#8;e;kMb_DDD]xufػK [2]8߾p[x(D8iKuN8sgʅӱ-[Je8M>{1\מIu]˻ 8ٙtgO ސV8pZu=b__3^?%;v.ӵka{ӝYȺu/T95(tg`2߇p[T)爗{Nݷ/-?@dDDDe$ΝYۜعL sg0E"bwgbOubi'b 6`Y%X[˝>k_HysSFb1EV絶޽ilh %$K&Bl.q˞"6 SP>6Ki#Lᅓ4ӓpx^fk"""EUrDDD\.F6`0R~;׈v^UBHpfZ-}ڎo;Y+$2Ch (PȾŲT~:d߯Z4c;~[> E)ëTvqF0q b}W]WS\kjldifi2}GzF6*D ==Dm>YuVCCԟPc}?b)c:e6:x#8l?X$b=Qw'&ך!>La"""7E`O+W2>7%|:{㵻"cSdfe2b:b,K:GP亿]_>#ۨ"g3,Y~MDDdiK=ߜLWޝu* vꩧlۖ-88e ]^RLqkk? G܁PBc\",""2q)X.(y1\ At>,޺@0ûvMsA浶c]ɵ8eg=P>k`rs[Z223tv|""24%GDD%[tF},O 34Em/CUs2*:<00h]'V gP(I&W;W#,{l֭"dCDD$pطfn4S )Az8a=:egm( ֈv}c*\x,;;~ϨC=P>,:떘:ֿyX_Yʜ5i =GDDFO%;vث_8Anio/bQػ7D8e= ƒ;2^LΨ~v |Ms3g'1&1'9dNi.3g356==mi={<Og=bK/l϶$Δ i^k+9BMsg@0MXqo_p3p|voߞit9^u] bn%""&""".H`m-K;:#s֊ƔOo> 2<07#0n~qmmSv٫V#.k st;XiV9&j\M!2VC(Ƕl/k<2efjh]y}$?l[lhZI/_NMsf\K0W>/#DkW"0JB)Zq#zFh(>rdGDD$I˩b(={y7qtÆOKNXAe8uzP4ӴRq۷X,0bn'wb9B*"""""""Wc]5u_ sHY'jeYtb+^{ȸ\>+X&0R] LOVa {*"2DY{G|ԝLBa#FV z6u)];V-_~5-|ч."""""2aFXK;,z1xfs$K;g}GcP:u>-cQ_@,}7)=ю xu?:{jTHB>J$L>7KIŪ{-ϙ$󾳏B;ob>mӒ4b00L]}إ~ w?]QB L^E& So}W5XŴ7] zozߎe f#Lv+]'>s[(iq} EZ5zs׀-c?wɟR i׾ٯo[IB>>sf̑}Hg߾LH%9|2\+lhcM|H:s?.|T|OD @}ZrS}Z>mE^|$ٷx!bRXk\ ??4$~K(:̑x/aH,o%ϴ(11G^#v ŗI-D$ԧeJO| [;,[qvK}׈yg?9dLee_B~MEd 0+\$m`y:.--+1ء!2n#~[?ww~-iɗˀ/OV`F|LmmY|ϰx3Vٷ[ڗa}j_NUTAYʬ" W`xrve.dodN>;o?GFJJ\p&LT.2$ވ^|/ޔo$z썘e0[9V^6 *Yۃ3&S\z"{˺esAμ|K$BœOէ u"w-|u w1b>3,i8?P97) *]֙~ un N{1^]p`i3 8] 糇οO ɿ8&P(>ܝ䟀ǰ.Ѳcsg7SӺ^vހN{??s7X+`c9u9`2)i9f% #/M䅓X $N3Eb WU!Y Jݵp! 3+|n75WWINYO>-4}:O܎kFxx #Wx۹[…v04f'q5NZnW^G}˚; S%YK h6%"S[EQƬU/ >q݉FmF p-R*PUaC0[߽.w4iɷӟ#,y؄,e)TX|`fm60mw ̿ V FXUXB}Z }z3dr&x6{]xp˱VcrJ]hݜ`u$zú4oOabi:N6V[J"2Mπ u^?jEb N総5nO6#o[ o̰Ln}:|fmz$O>J}Z8}>X{6kE䳥XO~LO_G ҧ)f]Na'")sÎV̿}/d_D1XV\O{iBr[YEaJ qE[)SeIKb>|*sG]MnM/#=F"OKO/<3’3!XMΧ>.}c}ҧhՓRa&02^5]̜sxٳ;&H)dh'"SVlTBE"k$ &n'[^O끱D\~F|IKOME %I?+wq~'oaԓsi(\,~%Ri_6ΰٯainEa@5򍟦.pۘyyW-vON93]NrX"םe>$9quLU `:k{O/}f.,8s՝F% 3#\ ԧeOŵ#XKK+NP}.}g4sY.i0?*`KbǘUE?eӌ@ Q԰,~s%/ GFR߇Q.Ҿ#Tfa\"P~#u[Yy HVK$.0 RN,R ק $QWۀV8}1i3T@J_p&̚-ts7ޙ#LL&91c2>y돑{g9O0p8a|!eKe)Hy7g I^\:OĽVcPґ>'EdiL*I2 SgZ4o[Ȥ0+>z;<h IDAT~DDF!u2),LHj`bbi!ֳHQODq8N?WR3>mw^VD&N Vh>`.A&aiXdPVϸ}~(4q#LS,/>:/!/xDzr󟎓k!}HlxUV>uFTϙEr~3D\}ZJ2ֹk"J`:\}D& \Jn{oJO&Om/*h'}4CGϷRxk*"2JEwXO$׉o$ZE}ZJtHhtJ-%d}7.@mZFXJ.VESԑS>#OSee6=u@[_ F'c)=d3#cTD$&,`JV5+Cc 0mk CtΡezT}ZJt9V=[=i5#D&ܽށՏ7GgZ5~D&Rso l*̉xlxhO_4 eSpDDF8#J '-f"x[8I\X&OgWҗ{.RYssp{4Q^vppf"ʴ˃v/&NQȵ|s-~Mx0>ٍa>iNl[My'RӜ'@mRFӧ]r_/^xyh'4|lG)=1;70_{d_Ve %}Dߺ֦鄪_]6cl~&BҎ!ק| (g`ᔙ?7|R=ʦHxcS۪OK ʭO'ƺ`ti6}ZLrJYWC{zvmBa>{-h/kCTTt٧2bn?k V[a 3m6}?8YRPVJbᔠX up;/2Ki)IIJ)#8|Xݷ>-SPY%\vS=pXɃV6,{ 0[1\sL '[#L) À@\ʫI= OFOK M+]]EJȀ0{l*%$|m・2R?c _5J`d-_}7k;R1b&0սtq)*PxRR[֕Lv~>-t >.**16.\~g65lS&"EXlġRb쁉է>=E-ۿyĎws~LF>-Lx_6iWK؟Mm5}ZD(' Ld*ԧu)^-&ӉWG. sV^ew),)yOE&'wVX"SFl0qz>لi0E{>SXi:FZK<@lx[_9R,E{UOβj΍f=7y:_ǎ߯.D׏Ms:ݚyK:8Zv1mN\.CsYĆGhg^ UwNa+dv<[j~n7M3]{-rHڛBt5ӨFs]G߽ sE;T~,SLE T]&6MHv雌x 31"ԝKClZu#W{/qgRt3+Us`CL6+ՋoNx?vo2f&ƌ6=~g_Z{`?nܯد3xwf$㥥K o@mf綴pǁ9)D js1v}ʹYs/&<"^cg[|8jI;LYQ{޻*O,њ^b)£w,o&S[)3gX̎ yMm#q U!㉈\/vnzQ~Sodƍ}r2:-7,R,ظիwsЇڄV/-]x;C:i:`4[v`Q7*c;(kWEs]nz(PC^Goz'x"""׋%ܺkFe%r08d|o4N}L[d??d ?1bǎrS};^zWNg>h0#A?y?12cw`g|?~ǀ¿LJTp0?~ęo|PSSƟsy{(?&S\.(z^`w_x^=٦D%Wrq 9^[&~bvUwDߺ#3~]Hix[x󟷷CCBd6 6d8%v?n;0'>޽GU߉$$B$1$\ (_T,Jub"P/뭻 Xk O—"T.I rL&s~L䜙3$dy$s|r3ܪw .;Ξ 8v7 /wx.CJL WXZ+} $筷H7O<Ӌ{A=I7-#2$;`y_~`IpקOR\5AU_:Cy￿s4=%n0ñ#I92N:NN0thKG^?C.$utt|\Ϟ%:%SQbbc]"D^Euqb+=gFZbߞ ᮯt71zFKrtƵ%)T;lqQRɔ 7B!ezQxk§:}:17aRӜL'Pɓ.^\vX/ёi&m2{_?EHb 1$lIsכ!<00 Xh=5v+q %󟖱y oƶ 'kmiW )b"RB!Dk8N:X1ݫѦ=/ZSO[)V+#hθNp?-]ܶ-daהS LQs&d[") :CГc4ku`Mw] !+ҡsr1PN VSfd`?DP{$<0FF8ʾsV w}A P2}bv?lVKB6<w)B%,Bnor]AXv)  lYYps\vm$tH*9,ؤ5.ճy^ e!u3G)Y|ӳP 4=f#s{4nʴi4rq۶@ !:$L" 'eZX9=t8jal޲Z4:b"3zaxHHLvמ^x` +=aI(]|S,\ͥ$ !=f#eT6RQVyyGŋ=K͙àG[Vd 曉<&P+] !| Cgwډ(xTP5UفjՎGv)bz]ENj/yލFr3س`4/qNrTSɭ:KJW7*hz9z _fU?4 J1/ RNg(8vwc$YWg:rT`Vmzm1h=69a?}f$zU;16_=fOcrNRXL=F=Yצ0}$w؀vkQ\2 oZROpdžB!"-+k/I$E3xzt\ڱKÈu8딿:_}YI5ޥj'Of֭X_w>}DΝ$GPw[-";ar+Y_CQl@ݯ亟uw!wrb~=Ug(xyu#{jYOCdS^Ȏj$@U}S,lW}_cotyEsWgUkޞs< [9>?mڻtԇ%l>\[1>3n`EK,\ʎc;26BEE[A 0j&27sh cEK86 #F>w.s皖q p91bz,6|u>={]ڻGa.2&Q[YJEݯ V8Vs2G(N󋘏{"܌ Q^h^A/kʫzZ+*Ohr. htId&ǒl 6B!\%߸+_~0j]a!^}yjv櫱cI6A q4uQTϧx"2- rXeW!"ڝ5(o'wܽxOVUl3ᮯW7R^=!DI(R!U}9_+ЌA{,^LքgVtuvBt[%š,SYO^B!B!zs+i ث{=ObeY VT\Zŕ%B!{B RrDYyb?'/Gmc(y|AL+2:=3Q !B!j0"WPU0>xkv&<ݯUY)R0 &^g׶SZ!B!I&Wżp'j}r`6wpEeڊ̀=UGKާE6 y!B!."'a*7^ڇ7?!DٸIM@)(ԥtEhs)6+&L <B "\$Lw&%[U<~Ex!2/ VD!B"gYaUз`j>D!B!Y$LWQU;Oŷ$A#B!BA$L4d BD.Y!B,2&f*X^4t6)'$Y*B!Dfa2)˼y͇" !zF~;ygB!T0\ņWFq5׍Jx'N}B!Bn"r+WS`EB`߈-!ϞKп ,QNB(u4gDz!HZOUA}B[EnD!u"Pػ_DQI&\<>[3]YV-p5o*4s տ o"Qɬ ż gpd*zi^΀%5\;Yƀe],)ӦMTbb}݅Ø[I7{Q_]}5 ^]`@^#֯79_BDMDޯgиF~R'ClB^S#T5ԔRDjKD)l}h>q.uk:׶aR`#*!&PZmBL&~9%.n,J{ * &p+r5#=]IͻBx3.g%7cwQ\zvTcoty^ۢ-kf0Ɓsͪ0w}k NTB7n0)G.nnvB$LZOeۊr&/z={P^eBBݏ&KΟ8Y!-}D8 ɔ 7B y_jlsa#W旖Xx{8~<9)0L]6dxj ;Qv_w]b\~>_]}5cn Oyds"oZGrW=B9w}!DjJ8sԝ NԜ?x$Mykl4 иѤrݲQ[NW^Ȣͥ,2pZgDgo'ڧ'& "^Euqb+E? 4r$3CIG/mQk,XcBtv߁d?2 GK8,YbD!:֪]^!9aZ;[ܴV;k֐:}:7dF"gH,!ӧ)[K{a]wSN3]G?9߄ ܤwz|}%:]N6 IDATEPNu7n+pWlu7͛12myh}B EEo`ғ%M{i!DTys>i5'm7њ!9L[fsa \?`DRy}=פ`b:Ղ-} p'"SUW#d/[s,f@{~7ɓ F;k֐0w(j%>'[tȊ17ndcg@^^Iʴi;670j&3v,~#CfG䴍~rF>}8T?rj)p2>mo@CfoI=U6^Q&ot' `(̽>|qV00OB+a8oIx;( FƴlCIM.oLs[g\? 1Qlw=ɒ'YRm^0nAwo_bGI/SpbqC򴄩;QR߼i Z1iݤ@V)3HLsetm=(j Ŭ8TO 0nX~~} K ,zp:y}Z6\œR9X^K::R#Hӥ򓷎\şd(j9Q3j`YwB+e; #I9w1$KL0!I!D瓄Ii>.I'> rNXђ* Iy'jzhJp'NhIx/+#8]*/IR8T>:t,xsP^ 3xo9X&\4)ё[3}{u7%Tl-7M8]ǁ: CFY=᎒j zES/W+Žj~C! w}{r]pPƙJMd.ZD5;d!Od;ěI\uuX6o9!D! ђ&͒%ZĊ;a%M@%BD2axq&ɒR=Ʉ@e?@eCsLjdO烱ɌN3sKeQ !o~Y/;Nk>GʉvOGO{wKaE}竪obbv_'^q4MAV lu΢5eTC/-)(!Xl E;DFΥIX+^J1?*X)?-M&hCrbh# !}% _{8N_rPm7 E_oHg\'mEڝ,_ӥr=`` %ѽ(hH )$&$jWp'z{ V iGT=fKմ0X0+V)YD߻D?)d4*<&fCslms2I#-zQɒA(I=D=lm/ѽX}İԯQɬz%i̺6g*(0Κ5Nnrvx?q r.6Ǎ;F{lW!D=L/契٩ (u8DTPU\.]D8F{g~ƱO0z{8dIhCD&|TĬSӲJ}9ZY'Z3fHXIb+ob˯G9x^~󏄻>ѳLW98uj-/Dp! ϵ5rq6sjCr- !zI_ѝGPFg&UNSYJv-q@\yq/K cDj(?6O::=[$L4u&diaVLV;:=!qHyu#iج2c[G=CWPVf,JHI>MO7;x[G?kG,y,\0 ۗ$ԉG$(wBDQA g)!+WnV}|;1""72|#ͥ1}\vZVXBŘ(۾q{WIK )O,f  z1SRe䯎PDfn}ӧs̙ϗm8DPg%iDQ@%ʝ@X(d-7߬5J2Fndwលlp&Zi|М$ym wHrF㜭q/;vA[r}*;{S\9]ClYYd2Coh :GT܉bT>ϞEZnV ꖥsrp"qzKJ^6+8X~,퀮s3Rl$\1ݝm^72GhFhl{U+X͏'whY*rJwI'u3cƥriG#J">_Scq4,[jQX7{8q)K>p,y,&LIw5O',Lz=c/*ȯ~ eeq}EГC? F?+tR iɓymF }\5-+vD(f we ښ(JrE]_º3Myi XaH1<l$DA7P u^~ w.fʝ2qJ1i.Vot#{}s7!Rg`Z(x`WN[9'%x˧\A|ns1)jEϮ_{8+Ma? ?p'z` 73d<xAӜ۰ygWK7ֲo$2y[n7ndc;٫ݻjXRMcЂ=sq4u#܌$WH5,ޕQ[3@EEU3u.Mu(dKM WB RAm%+=3//x"KknEe1mLL7LNzCgyo)8v1@-]+%6]rΛ_Vd L-Ó߻4qQOSZg7Y"1.@ת'Ued^@}W 'PP&k!DWkpgw/^Yu@5PTs?5S~V gL]u $EE3/Xi=3v,CR gsrH5?1֯dJRp".iRX!EbQ!JL WX%>ΗW^I{B™3x]2|gȋIFO̙܍ L1x؋= =]GٳF[=ewI}ÿO>aڔBtI!B$IȑT 'O;{L Li/=p، CB-+ -[Lo§mpGB'2.5T2cXʆ g@fr,y#sd26=U˾ZV:KUQRͦ*rR>f7]ٗ1(ns}&29'K㽽Y97'5{Me>f$Šzu%~jaָTFǓ@fe|YRͻ{QXQܮvQ'pӕ}LQ\i{N؀cQ!p.?o9ɓ7liٵkM'N8nk{t~F<8x_~jC/!D]bDQǻwv\νsvR/}HH`˯Gx-#KnF\ ܘΌG>H`lVc84)o\Xx`C~Lǝ#៾`rNϝ.74>,!9I,r_9I<1)ż8VfDX5(fc>zግPbQ!X`r>MSu5Wc#"SRHfJHI4i^Qݜ|2-–-[PNW@ɹY,PͲ !~`UM7<(Ezd&DzLIWGި f%F\ߒI8W6xzfLIb+|Z9}bIJ!껞dIaE= xm{9.'+^2֣k>f{mHhCh`VAcR7!"sr1PN VUT}'fv-I'z'Ř/$:%1,^̾ٷp'_wlƐ4Btar+iJ|]/Ԩ1ϓfۃ gi{] X!7#’ۇzb@um*UNf>b(ǿ.i̽>ק3 YżRrR< ͥ{jOa1{jgNijO{bOOO=,o(=M1ݧθ&-1׶?}SOU)+ =C,>uy R!VAJw %&1[:}:ɅO>X47g.YI#ޣ?)l߳NOJ!ABtJEQ҂3`DW7&4UNskRח0D%[jZtȹ8?INjgŠz}qWz-kzheo6Gxs'1TOLB!8Dʀ`;eFqN'ﺋo |(|(-='SWgup&d9 _I+`Dxb]ؽ/OЫz!CBCO!a%ĥɶ:s3<p *8֒џ-ܿK!Btݻ9<3grw9(V+t}f{:ٿ&$#n5s|+CG_{5 p.BѱwgϢXXa+Vx՞99\LxɂPd<76QX 5_{ޣ!O=7dɓdTu/.u*9?ZV6,>EX~> Bfrd@ cBSk IDATxe|lk % rR&k?ӗߥB!:VSm-_OLo0bs>wiyy2jUA)?1xeⲳogτ m|-5⪫#v`sTc<Ҧ咅/rVQ6M|.DQpTK)EO4jw@K@_ɾSxX+T7y5֌ߥBWcǒ:m,0̥8}C(yY WFC.$s"TJL W<, v`?Lj8m}|3d= ~cⲳ ۰d,r&ixX^Ӳ>QWC/u VG[F7"q , 2qj$'wB/(9B!pO@zfjP1C3f+fZNno&K}m{ RO'6#k$Ckhyk{:xIN@#j`l.:$aý'&e`ZIco.mDSVfl2cY1 2^ żIƅ4ee!+g316ק14Ӳ Ȏjm#MިdlV nȼTX5GM;6z/e׎EfBt/*^̙w #kefzN'oڵ-_'LTCL( W܄ʝܽ<3X1TW.#VHPyUJuwՍLy3{HΎjvWs<oŕ X}$@~ûVי+Ճ̺6y~ ãZU)+ um* ol:dśKY¤ FMվu!:$E =_TCj盻VLt ,kڼiT\!wJJ! 0Q%/X1}YI `U2=¦*.ER8֛_ <3q-Q01 A+[YwC?O vG+5ǘXbU]$Y=CtʫT7Qp,Ħv=UǎꐆttlP p)"[n\dKiiZZDĉdlMkl[YAM^ F'#7a"®c&ZcGIuدT;~tp'Pry9=hr_ڇCh-BD4鋚< Lkj(#Zh@|݀w&'#x!A).rCNiȧW -"ZWMÐ ѹL; Ŵiv,2!D3n$LTI!"-#W0%=Mm[Kug?ZbZD4п p"lUJKo)հ՝74UajBtWuUKe&21K?=ty^}gR=L}(@ˍnknl4KLZLknt{"rTލo,?ߊݸԖ^&&{Oy"D7 ^Ҋz?20۫.V,`%O[GgBtV.>MYwʣΈ-D*;A>ZJLHzL~ ܫC`һ!9.Zb Pir|%UA*BD<%NPYɽ+m5NW0 X[śK%y/DWT]Virsr~\TbqgR%EimLMx%Lb}kDք#/7&LHr[t?)y:iIUUP,ԥtEhs)*w0sf%<{*RD -7\:?#Zr)1-"HbZ~+~)BtmN=0cys>y+c﹈?p MHUww]aBte,siJz* 5k {.!rSv&ך HLHvV!K w 4-1䵹czά:%Kug}x.sEEyTiJΝgxO+]ͬ;1oly<ĴLi&,<:k1]&K..EU[ZT|Vb>ߒ0Qu!9aܾz K GGt5UJj*p9Zx߈oOQùӢ>&OIp&%Z4صZCLsj"55%BTtO&$L+Y|ftTv?S] Lθ'+d#C0} 1-"Kbڻ-1#:kҺmqcKUcѲjߒ0Q-'BN.gz TB*G>^Ñ o Dq6\a2{WIA罆K¥Ge.;es⵸eƮY1^RZnqޭ-UbZtǴڶwOop?JDtUg!Ũ>%ib >CLP~0$UeP{޸A7NzDD`˘0)\r30 ?{-HirߴT=f'xH|'Q;F.\`x#n]}1,= |3`)DX8p'V*y,7<|/XoijCy|^pwh+ƿ>a^Շc^ѣy7zOj8i_"?U5ҝV>o(XgOH+xf-7g0[зZo]x߈KLnB}armV;P՛j0Ʀ10X;|~NM: Y݉s.g;W-\bGUfз@T {j*eX%km_Gߴ8WiD}F>DPUwû妡D˃HKi5VjW@G~]\ wdΒ:E-l%Y%U%翢ii ֪2ItU}N};gY^l^,`Gqݴ(t}@}VJέ(󽨠q(!AY㾯׭|5}VZyַtԝ,\,D`Kkzhg9 K.g_"N_R0;cbZk-iZ | ҵq Zk'$ˁUe'-uͧ5Y?ls\:1g,QўFU^ 5 a/<9 א0G]9;-Q$ YȰH}G]Zߓ=3TaZZiJ{wcq.J˷͟S.p}eY7`965Q!&BFYNy}[nN?fD!NzӢtNLk{K?@20J_^1@?B=+j.W}x7MgZO&oi&w(+Xn& 'i5uTQ? N.jҜUJ =US#TWĥSUtӏdxt}+/p#Q1Y-ŴVE x@8n)ni)7_ln檵kK}Kiq:/5މn{ 5'kmͯp? zr~;>'VX;FXַcYm{ߘ>RWcK;]͉Kgsi69yJz>ySꚳG?ŕ4ֵ*n"tM[?mOt.7jB$pYl@-pyi~yeAXܽt7+g'>%bo*ha!D7u 3TqcjZ&sg2߰Daiy?Oa@X莎svu%-*zFbŝ@܀;7#Z|oFqf Ei>`Nc۽e#Dۜ()Do{-ɒ@0kq#%ILW qvſ$LZG0lg % bBDUg&moguPӀW&%vG`lqVi7,]y9cr?wbQBoϱ!VSk쑳>Xjf-7Z+|K1 =VyS &F%^9%;{h-EڃӃeIJ>~"|u\,CΝ KK¤u-]-]'<ǀ+GH=]KWʧuˮ&Z]%L7S^^Y7oh@]cmh]ljmi!PMcRbZt΋iMLnk \\{U|[a02dâ~vŰrMc:e  Ibqt\Ĵ$z:uǿ$LZG!o's|&WMKB@!8jMQKhcKZn8EaEsl_EݰhWڍʓɠOOB!h)bvl fDn-7tHLu11[tsYm6BW;8h}YCZi:,C ;Nd`z 66/ ! 1:lIWcDK/'wԾS?^V=xq҉(('8g)JɾiC*͖ϱ^ hj"UZnMK"j-7vWムZOfC~6o|0Fgfql[ 3HL)8\EY r?^ Xwa߬6-?|?x!Dd{ent,1vgu~ $B|u~ޟWڤ}KŻGG_=håkG\JLc;)mx?`F!ݛe}˺w<7AQeĴ ΥX@nN5?xACgL4F FMBDוּwZ]iǴo;ދس\6ϧx߰h \Xk}+O(]c3gjMˍvajowm͸ĴhHi1^ǵBIcZiǼ[i:c;ؤ+pƲFbZtNAv9&Gq0ɱ^f6y!mZBȤ}hjl}.nE>*vTuF',1i]0Kϱo@;?`?lyҲA U*Y&Ph)H#Ix՝+ʕrpA];H|UnJA6Żs;}wp^ywϳԼTϾ)89V=˪9Ydn.Oz+aVvSq9 =;''uV>Ԋ<̀WY[4)\ן >҆WdzK2*տ]ߩYJ)6gjטV> )F)r.;XY+U4,'~$&\߽Pi$nݔQw``559_&ͮ|'z^`6Z+3_-^rzi, jP+N޼ʭwƆ4f '+D uŭ}vu/ɖ&{^^5gy0c}yDN#2'YGXr+I{LҎZ]J6_aql3t]ϳoeV(7^y6*.|JNrs 0)7ܝ̩9,lLԦI >/j|U>SuCN1 `Einӱ|w]t&FGk`5膼XOQp0`P @&%͎  IDAT 0, I^KzZi)[5L}Uu%}(iGPTcI+t%1T-czf[ %+u'I_Jg}I'μH[uύR_+Qے>Rae^ʼlI:TSI%L\;frWa[T3<)w^g}V{b I&F=wǙ_-*XƑCM[H)t_~]J.[s_W\~{xu%ryn38ﶤ_i7s!}/Uo_a`3ZZK/cŦZg}&5_rw4? `Ex%Ey8u#u0wt)jm\(Ϋ(l)C[T`MK]岧TyHNJ=RjFՕYO$9bX>+~:ԑu$`ZFNL:/Wo7;mS̋Ed߶VeXyۥ "q+vL:KiN^}7,YXB&>kۓu#09;F17b /ߎLv; ago3K+\jYto;)opC>50@rbqq-5[}s}]]bήBK1\]X'w䟝nbN82b GFK)uRxXdwt C!(JΤ[c'V ku˞?QU<+ʡ( .T-"/^{ MůNJݥFbbN܉9`ur'۸)]l;>uϱbR/Dc-sdNly魃':g WmГ3~qduxLLr=g8 ]²Wy͜}.uNϾ1b+ r.fl:=UW[74n3>+uEk5{;JUl?,kIzW좯uNϾ\ ǒ>w%] =l$kōB֡_܈ؗ͝Fl!u=RHn}ڔkM zNoGN|nNl,/uh:{w)j{]+LJޞ8/Sg;#3_O'T<$1΋.حE.6pѫsRe_X.>{-\yr(Ǫ%YgA_Dk<]ߺ<݊qC&@&R)k#VdbC'kL-bؘ\vQNT^_Ru^>+#UF@n@ɮk=ŠqCۀ bXnQIվ'Rl-Ӳj{u*1:HTy)uڭs}Vu]mt[۲«bVڊqCɮ3?`r#5j?_M-[z#/:%zmˮ3r⏜X9R}^ڮCx:g1C{ߋC* aX%]d?!Y9qXoˆ[2N$RxGo{ϝ3ؕ}Ůμ8 2/WՉ3}쵤Ord̯$ޙݗ/ߔۅw; V|ZxRGz9NmX^ -ȝ3-+2x>&]gdĦBuh/[g/}AQ:*jgbSWhzα7K:#w?sbiq|fh"e;Mնۧx9E΋. o`l,hpeXrmˮ32bSe:ڭne}<ܒ=R]D (*;PμcN~LOJz>B/UT(Z?ynQ4&&eg}k~&/6Ey)\u_KzZw5oO-BL\@cIVBuh/[g)>rw%^/ $grxٻ"\_VL_-SW^(=s#ur&{oڌEJy!zYT댌y)N:ڭs}vAXy~#@~?2Z^*~PIxްʱd;Q/3m5svy1CYBmֿ\znĞ_Aj?x2/i/_žg+c--=:9Xɴ(l+lk{:r^aŞ]2.gL?|,"/z.[gO_O˟k],VI_1]w;{?Viʻ-|]АDk5g3٨} 6@#3}z3AL_]dtp1`@?~oLJcL|{.KhȺ~r~ pxygs'.rЈyZwC{hivB.Kh ]}t)zՒ} :DWPҞ'$I7盛} vu;;uIߖUsIO  /JW1kpIENDB`pmemkv-1.5.0/doc/libpmemkv.3.md.in000066400000000000000000000266241410000423300166340ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV, 3) collection: libpmemkv header: PMEMKV secondary_title: pmemkv ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2019-2021, Intel Corporation) [comment]: <> (libpmemkv.3 -- man page for libpmemkv) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[ERRORS](#errors)
[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 pmemkv iterator API description see **libpmemkv_iterator**(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. Note: There are no explicit upper_bound/lower_bound functions. If you want to obtain an element(s) above or below the selected key, you can use pmemkv_get_above() or pmemkv_get_below(). See descriptions of these functions for details. `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 open (successful or failed). `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`. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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`. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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`). Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `int pmemkv_get_below(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 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. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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. Order of the elements is specified by a comparator (see **libpmemkv**(7)). `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 # The following example is taken from `examples/pmemkv_basic_c` directory. Basic pmemkv usage in C: ```c @C_EXAMPLE@ ``` ## Common usage mistake ## Common mistake in pmemkv API usage (especially when using C++ API) is to dereference pointer to the data stored in pmemkv outside of a callback function scope. ```cpp std::string value; const char* ptr; size_t sz; kv->get("key1", [&](string_view v) { /* Save pointer to the data to use it later outside of a callback scope */ ptr = v.data(); sz = v.size(); }); kv->remove("key"); /* ERROR! * Using this pointer outside of a callback function may cause access to some random data * or a segmentation fault. At that point, ptr should be considered as invalid. */ value.append(ptr, sz); ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv_config**(3), **libpmemkv_iterator**(3) and **** pmemkv-1.5.0/doc/libpmemkv.7.md000066400000000000000000000227251410000423300162310ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV, 7) collection: libpmemkv header: PMEMKV secondary_title: pmemkv ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2019-2021, Intel Corporation) [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. Most sorted engines also support passing a custom comparator object (see **libpmemkv_config**(3)). By default, pmemkv stores elements in binary order (comparison is done using a function equivalent to std::string::compare). Persistent engines usually use libpmemobj++ and PMDK to access NVDIMMs. They can work with files on DAX filesystem (fsdax) or DAX device. # 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. To configure an engine, pmemkv_config is used (**libpmemkv_config**(3)). Below is a list of engines along with config parameters they expect. Each parameter has corresponding function (pmemkv_config_put_path, pmemkv_config_put_comparator, etc.), which guarantees type safety. For example to insert `path` parameter to the config, user should call pmemkv_config_put_path(). For some use cases, like creating config from parsed input, it may be more convenient to insert parameters by their type instead of name. Each parameter has a certain type and may be inserted to a config using appropriate function (pmemkv_config_put_string, pmemkv_config_put_int64, etc.). For example, to insert a parameter of type `string`, `pmemkv_config_put_string` function may be used. Those two ways of inserting parameters into config may be used interchangeably. For description of pmemkv core API see **libpmemkv**(3). ## 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). 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). Note that when using poolset file, size should be 0. It's used to open or create pool (layout "pmemkv"). + type: string * **create_if_missing** -- If 1, pmemkv tries to open the pool and if that doesn't succeed, it creates it. If 0, pmemkv will rely on **create_or_error_if_exists** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **create_or_error_if_exists** -- If 1, pmemkv creates the file (but it will fail if path exists). If 0, pmemkv will rely on **create_if_missing** flag setting. If both **create_\*** flags will be false - pmemkv will open the pool (unless the path does not exist - then it'll fail). + type: uint64_t + default value: 0 * **size** -- Only needed if any of the above flags is 1. It specifies size of the database [in bytes] to create. + 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 four possible combinations of parameters (where '-' means 'cannot be set'): | **#** | **path** | **create_if_missing** | **create_or_error_if_exists** | **size** | **oid** | | ----- | :------: | :-------------------: | :---------------------------: | :------: | :-----: | | **1** | set | 0 | 0 | N/A | - | | **2** | set | 1 | - | set | - | | **3** | set | - | 1 | set | - | | **4** | - | N/A | N/A | N/A | set | >*ad 1*: If none of the flags are set (default flags' value is false), pmemkv will only try to open the file > and it fails if the path does not exist. Size is ignored. >*ad 2*: If **create_if_missing** flag is set, pmemkv will try to open the file (based on path) > and if it doesn't succeed, pmemkv tries to create the file. > Flag **create_or_error_if_exists** can't be set (or it should be set to 0). >*ad 3*: If **create_or_error_if_exists** flag is set, pmemkv will try to create the file (and fails if it exists). > Flag **create_if_missing** can't be set (or it should be set to 0). >*ad 4*: If **oid** is set, path should not be set. Both flags and size are ignored. 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 for cmap engine and "pmemkv_\" for other engines (e.g. "pmemkv_stree" for stree engine). 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 and TBB 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: 16777216 (16MB) (value MEMKIND_PMEM_MIN_SIZE is specified in memkind.h) ## 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 package is 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: 16777216 (16MB) (value MEMKIND_PMEM_MIN_SIZE is specified in memkind.h) * **comparator** -- (optional) Specified comparator used by the engine + type: object ## 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 . Some of them (radix, tree3, stree and csmap) 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 + Node.js - for details see + Python - for details see + Ruby - for details see # SEE ALSO # **libpmemkv**(3), **libpmemkv_config(3)**, **libpmemkv_iterator**(3), **pmempool**(1), **libpmemobj**(7) and **** pmemkv-1.5.0/doc/libpmemkv.Doxyfile.in000066400000000000000000000230051410000423300176440ustar00rootroot00000000000000#--------------------------------------------------------------------------- # 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 = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which the alphabetical index list will be split. COLS_IN_ALPHA_INDEX = 1 #--------------------------------------------------------------------------- # 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 = @PMEMKV_ROOT_DIR@/src INPUT += @PMEMKV_ROOT_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 = @PMEMKV_ROOT_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 = @PMEMKV_ROOT_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 = #--------------------------------------------------------------------------- # 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 WARN_AS_ERROR = @DOXYGEN_WARN_AS_ERROR@ pmemkv-1.5.0/doc/libpmemkv_config.3.md.in000066400000000000000000000257121410000423300201560ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_CONFIG, 3) collection: libpmemkv header: PMEMKV_CONFIG secondary_title: pmemkv ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2019-2021, Intel Corporation) [comment]: <> (libpmemkv_config.3 -- man page for libpmemkv configuration API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[ERRORS](#errors)
[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_size(pmemkv_config *config, uint64_t value); int pmemkv_config_put_path(pmemkv_config *config, const char *value); int **deprecated** pmemkv_config_put_force_create(pmemkv_config *config, bool value); int pmemkv_config_put_create_or_error_if_exists(pmemkv_config *config, bool value); int pmemkv_config_put_create_if_missing(pmemkv_config *config, bool value) int pmemkv_config_put_comparator(pmemkv_config *config, pmemkv_comparator *comparator); int pmemkv_config_put_oid(pmemkv_config *config, PMEMoid *oid); 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_object_cb(pmemkv_config *config, const char *key, void *value, void *(*getter)(void *), 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); pmemkv_comparator *pmemkv_comparator_new(pmemkv_compare_function *fn, const char *name, void *arg); void pmemkv_comparator_delete(pmemkv_comparator *comparator); ``` 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_size(pmemkv_config *config, uint64_t value);` : Puts `value` to a config at key `size`, it's required when creating new database pool. This function provides type safety for **size** parameter. `int pmemkv_config_put_path(pmemkv_config *config, const char *value);` : Puts `value` to a config at key **path**. It's a path to a database file or to a poolset file (see **poolset**(5) for details), to open or create. Note that when using poolset file, size should be 0. This function provides type safety for `path` parameter. `int pmemkv_config_put_force_create(pmemkv_config *config, bool value);` : It is **deprecated** and kept for compatibility. It's an alias for `pmemkv_config_put_create_or_error_if_exists` - use it instead. `int pmemkv_config_put_create_or_error_if_exists(pmemkv_config *config, bool value);` : Puts `value` to a config at key `create_or_error_if_exists`. This flag is mutually exclusive with **create_if_missing**. It works only with engines supporting this flag and it means: If true: pmemkv creates the pool, unless it exists - then it fails. If false: pmemkv opens the pool, unless the path does not exist - then it fails. False by default. `int pmemkv_config_put_create_if_missing(pmemkv_config *config, bool value)` : Puts `value` to a config at key `create_if_missing`. This flag is mutually exclusive with **create_or_error_if_exists**. It works only with engines supporting this flag and it means: If true: pmemkv tries to open the pool and if that doesn't succeed it means there's (most likely) no pool to use, so it tries to create it. If false: pmemkv opens the pool, unless the path does not exist - then it fails. False by default. `int pmemkv_config_put_comparator(pmemkv_config *config, pmemkv_comparator *comparator);` : Puts comparator object to a config. To create an instance of pmemkv_comparator object, `pmemkv_comparator_new()` function should be used. `int pmemkv_config_put_oid(pmemkv_config *config, PMEMoid *oid);` : Puts PMEMoid object to a config (for details see **libpmemkv**(7)). `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_put_object_cb(pmemkv_config *config, const char *key, void *value, void *(*getter)(void *), void (*deleter)(void *));` : Extended version of pmemkv_config_put_object. It accepts one additional argument - a `getter` callback. This callback interprets the custom object (`value`) and returns a pointer which is expected by pmemkv. Calling pmemkv_config_put_object_cb with `getter` implemented as: ``` void *getter(void *arg) { return arg; } ``` is equivalent to calling pmemkv_config_put_object. `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. `pmemkv_comparator *pmemkv_comparator_new(pmemkv_compare_function *fn, const char *name, void *arg);` : Creates instance of a comparator object. Accepts comparison function `fn`, `name` and `arg. In case of persistent engines, `name` is stored within the engine. Attempt to open a database which was createad with different comparator of different name will fail with PMEMKV_STATUS_COMPARATOR_MISMATCH. `arg` is saved in the comparator and passed to a comparison function on each invocation. Neither `fn` nor `name` can be NULL. `fn` should perform a three-way comparison. Return values: * negative value if the first key is less than the second one * zero if both keys are the same * positive value if the first key is greater than the second one The comparison function should be thread safe - it can be called from multiple threads. On failure, NULL is returned. `void pmemkv_comparator_delete(pmemkv_comparator *comparator);` : Removes the comparator object. Should be called ONLY for comparators which were not put to config (as config takes ownership of the comparator). To set a comparator for the database use `pmemkv_config_put_object`: ```c pmemkv_comparator *cmp = pmemkv_comparator_new(&compare_fn, "my_comparator", NULL); pmemkv_config_put_object(cfg, "comparator", cmp, (void (*)(void *)) & pmemkv_comparator_delete); ``` ## 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 examples are taken from `examples/pmemkv_config_c` directory. ## BASIC EXAMPLE ## Usage of basic config functions to set parameters based on their functionalities, e.g. `pmemkv_config_put_path()` or `pmemkv_config_put_size()`. ```c @CONFIG_BASIC_EXAMPLE@ ``` ## TYPE BASED CONFIGURATION EXAMPLE ## Usage of config functions to set and get data based on their data type, e.g. `pmemkv_config_put_int64()` or `pmemkv_config_put_object()`. ```c @CONFIG_TYPE_BASED_EXAMPLE@ ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3) , **libpmemkv_json_config**(3) and **** pmemkv-1.5.0/doc/libpmemkv_iterator.3.md.in000066400000000000000000000232721410000423300205410ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_ITERATOR, 3) collection: libpmemkv header: PMEMKV_ITERATOR secondary_title: pmemkv ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2020-2021, Intel Corporation) [comment]: <> (libpmemkv_iterator.3 -- man page for libpmemkv iterators API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[ERRORS](#errors)
[EXAMPLE](#example)
[SEE ALSO](#see-also)
# NAME # **pmemkv_iterator** - Iterator API for libpmemkv This API is **EXPERIMENTAL** and might change. # SYNOPSIS # ```c #include int pmemkv_iterator_new(pmemkv_db *db, pmemkv_iterator **it); int pmemkv_write_iterator_new(pmemkv_db *db, pmemkv_write_iterator **it); void pmemkv_iterator_delete(pmemkv_iterator *it); void pmemkv_write_iterator_delete(pmemkv_write_iterator *it); int pmemkv_iterator_seek(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_lower(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_lower_eq(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_higher(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_higher_eq(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_to_first(pmemkv_iterator *it); int pmemkv_iterator_seek_to_last(pmemkv_iterator *it); int pmemkv_iterator_is_next(pmemkv_iterator *it); int pmemkv_iterator_next(pmemkv_iterator *it); int pmemkv_iterator_prev(pmemkv_iterator *it); int pmemkv_iterator_key(pmemkv_iterator *it, const char **k, size_t *kb); int pmemkv_iterator_read_range(pmemkv_iterator *it, size_t pos, size_t n, const char **data, size_t *rb); int pmemkv_write_iterator_write_range(pmemkv_write_iterator *it, size_t pos, size_t n, char **data, size_t *wb); int pmemkv_write_iterator_commit(pmemkv_write_iterator *it); void pmemkv_write_iterator_abort(pmemkv_write_iterator *it); ``` For general description of pmemkv and available engines see **libpmemkv**(7). For description of pmemkv core API see **libpmemkv**(3). # DESCRIPTION # Iterators provide methods to iterate over records in db. Both iterator types (pmemkv_iterator (read) and pmemkv_write_iterator) allow reading record's key and value. To use pmemkv_write_iterator as a pmemkv_iterator you need to get its member "iter" (write_it->iter). Example of calling pmemkv_iterator_seek_to_first() with both iterator types. ```c /* read_it is already created, by pmemkv_iterator */ int status = pmemkv_iterator_seek_to_first(read_it); /* write_it is already created, by pmemkv_write_iterator */ int status = pmemkv_iterator_seek_to_first(write_it->iter); ``` A pmemkv_write_iterator additionally can modify record's value transactionally. Some of the functions 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. Holding simultaneously in the same thread more than one iterator is undefined behavior. `int pmemkv_iterator_new(pmemkv_db *db, pmemkv_iterator **it);` : Creates a new pmemkv_iterator instance and stores a pointer to it in `*it`. `int pmemkv_write_iterator_new(pmemkv_db *db, pmemkv_write_iterator **it);` : Creates a new pmemkv_write_iterator instance and stores a pointer to it in `*it`. `void pmemkv_iterator_delete(pmemkv_iterator *it);` : Deletes pmemkv_iterator. `void pmemkv_write_iterator_delete(pmemkv_write_iterator *it);` : Deletes pmemkv_write_iterator `int pmemkv_iterator_seek(pmemkv_iterator *it, const char *k, size_t kb);` : Changes iterator position to the record with given key `k` of length `kb`. If the record is present and no errors occurred, returns PMEMKV_STATUS_OK. If the record does not exist, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_lower(pmemkv_iterator *it, const char *k, size_t kb);` : Changes iterator position to the record with key lower than given key `k` of length `kb`. If the record is present and no errors occurred, returns PMEMKV_STATUS_OK. If the record does not exist, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_lower_eq(pmemkv_iterator *it, const char *k, size_t kb);` : Changes iterator position to the record with key equal or lower than given key `k` of length `kb`. If the record is present and no errors occurred, returns PMEMKV_STATUS_OK. If the record does not exist, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_higher(pmemkv_iterator *it, const char *k, size_t kb);` : Changes iterator position to the record with key higher than given key `k` of length `kb`. If the record is present and no errors occurred, returns PMEMKV_STATUS_OK. If the record does not exist, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_higher_eq(pmemkv_iterator *it, const char *k, size_t kb);` : Changes iterator position to the record with key equal or higher than given key `k` of length `kb`. If the record is present and no errors occurred, returns PMEMKV_STATUS_OK. If the record does not exist, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_to_first(pmemkv_iterator *it);` : Changes iterator position to the first record. If db isn't empty, and no errors occurred, returns PMEMKV_STATUS_OK. If db is empty, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_seek_to_last(pmemkv_iterator *it);` : Changes iterator position to the last record. If db isn't empty, and no errors occurred, returns PMEMKV_STATUS_OK. If db is empty, PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_is_next(pmemkv_iterator *it);` : Checks if there is a next record available. If true is returned, it is guaranteed that pmemkv_iterator_next(it) will return PMEMKV_STATUS_OK, otherwise iterator is already on the last element and pmemkv_iterator_next(it) will return PMEMKV_STATUS_NOT_FOUND. `int pmemkv_iterator_next(pmemkv_iterator *it);` : Changes iterator position to the next record. If the next record exists, returns PMEMKV_STATUS_OK, otherwise PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_prev(pmemkv_iterator *it);` : Changes iterator position to the previous record. If the previous record exists, returns PMEMKV_STATUS_OK, otherwise PMEMKV_STATUS_NOT_FOUND is returned and the iterator position is undefined. It internally aborts all changes made to an element previously pointed by the iterator. `int pmemkv_iterator_key(pmemkv_iterator *it, const char **k, size_t *kb);` : Assigns record's key's address to `k` and key's length to `kb`. If the iterator is on an undefined position, calling this method is undefined behaviour. `int pmemkv_iterator_read_range(pmemkv_iterator *it, size_t pos, size_t n, const char **data, size_t *rb);` : Allows getting record's value's range which can be only read. You can request for either full value or only value's subrange (`n` elements starting from `pos`). Assigns pointer to the beginning of the requested range to `data`, and number of elements in range to `rb`. If `n` is bigger than length of a value it's automatically shrunk. If the iterator is on an undefined position, calling this method is undefined behaviour. `int pmemkv_write_iterator_write_range(pmemkv_write_iterator *it, size_t pos, size_t n, char **data, size_t *wb);` : Allows getting record's value's range which can be modified. You can request for either full value or only value's subrange (`n` elements starting from `pos`). Assigns pointer to the beginning of the requested range to `data`, and number of elements in range to `wb`. If `n` is bigger than length of a value it's automatically shrunk. Changes made on a requested range are not persistent until *pmemkv_write_iterator_commit()* is called. If the iterator is on an undefined position, calling this method is undefined behaviour. `int pmemkv_write_iterator_commit(pmemkv_write_iterator *it);` : Commits modifications made on the current record. Calling this method is the only way to save modifications made by the iterator on the current record. You need to call this method before changing the iterator position, otherwise modifications will be automatically aborted. `void pmemkv_write_iterator_abort(pmemkv_write_iterator *it);` : Aborts uncommitted modifications made on the current record. ## ERRORS ## Each function, except for *pmemkv_iterator_delete()*, *pmemkv_write_iterator_delete()* and *pmemkv_write_iterator_abort()*, returns one of the pmemkv status codes. To check possible options see **libpmemkv**(3). # EXAMPLE # The following example is taken from `examples/pmemkv_iterator_c` directory. Usage of basic iterator functions to iterate over all records and modify one of them. ```c @ITERATOR_BASIC_EXAMPLE@ ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3) and **** pmemkv-1.5.0/doc/libpmemkv_json_config.3.md000066400000000000000000000045531410000423300206020ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_JSON_CONFIG, 3) collection: libpmemkv header: PMEMKV_JSON_CONFIG secondary_title: pmemkv_json_config ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2019-2021, Intel Corporation) [comment]: <> (libpmemkv_json_config.3 -- man page for libpmemkv_json_config configuration API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[ERRORS](#errors)
[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 using this API can be found in **libpmemkv_config**(3). # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3), **libpmemkv_config**(3) and **** pmemkv-1.5.0/doc/libpmemkv_tx.3.md.in000066400000000000000000000055201410000423300173370ustar00rootroot00000000000000--- layout: manual Content-Style: 'text/css' title: _MP(PMEMKV_TX, 3) collection: libpmemkv header: PMEMKV_TX secondary_title: pmemkv ... [comment]: <> (SPDX-License-Identifier: BSD-3-Clause) [comment]: <> (Copyright 2020-2021, Intel Corporation) [comment]: <> (libpmemkv_tx.3 -- man page for libpmemkv transactions API) [NAME](#name)
[SYNOPSIS](#synopsis)
[DESCRIPTION](#description)
[ERRORS](#errors)
[EXAMPLE](#example)
[SEE ALSO](#see-also)
# NAME # **pmemkv_tx** - Transactions API for libpmemkv This API is **EXPERIMENTAL** and might change. # SYNOPSIS # ```c #include int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx); int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb); int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb); int pmemkv_tx_commit(pmemkv_tx *tx); void pmemkv_tx_abort(pmemkv_tx *tx); void pmemkv_tx_end(pmemkv_tx *tx); ``` # DESCRIPTION # The transaction allows grouping `put` and `remove` operations into a single atomic action (with respect to persistence and concurrency). Concurrent engines provide transactions with ACID (atomicity, consistency, isolation, durability) properties. Transactions for single threaded engines provide atomicity, consistency and durability. Actions in a transaction are executed in the order in which they were called. `int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx);` : Starts a pmemkv transaction and stores a pointer to a *pmemkv_tx* instance in `*tx`. `int pmemkv_tx_put(pmemkv_tx *tx, 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 the key `k` and `vb` is the length of value `v`. When this function returns, caller is free to reuse both buffers. The inserted element is visible only after calling pmemkv_tx_commit. `int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb);` : Removes record with the key `k` of length `kb`. The removed elements are still visible until calling pmemkv_tx_commit. This function will succeed even if there is no element in the database. `int pmemkv_tx_commit(pmemkv_tx *tx);` : Commits the transaction. All operations of this transaction are applied as a single power fail-safe atomic action. `void pmemkv_tx_abort(pmemkv_tx *tx);` : Discards all uncommitted operations. `void pmemkv_tx_end(pmemkv_tx *tx);` : Deletes the pmemkv transaction object and discards all uncommitted operations. ## ERRORS ## Each function, except for *pmemkv_tx_abort()* and *pmemkv_tx_end()* returns status. Possible return values are listed in **libpmemkv**(3). # EXAMPLE # The following example is taken from `examples/pmemkv_transaction_c` directory. Usage of pmemkv transaction in C: ```c @TRANSACTION_BASIC_C_EXAMPLE@ ``` # SEE ALSO # **libpmemkv**(7), **libpmemkv**(3) and **** pmemkv-1.5.0/examples/000077500000000000000000000000001410000423300146155ustar00rootroot00000000000000pmemkv-1.5.0/examples/CMakeLists.txt000066400000000000000000000121671410000423300173640ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # examples/CMakeLists.txt - CMake file for building all examples along with # the current pmemkv sources, for verification purposes. # To build an example as a standalone application (with pmemkv from the system) # see a CMake file in any of the subdirectories. # add_custom_target(examples) # ----------------------------------------------------------------- # ## Setup examples # ----------------------------------------------------------------- # add_common_flag(-Wno-unused-but-set-variable) add_common_flag(-Wno-maybe-uninitialized) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) add_dependencies(examples pmemkv) # Add developer checks add_cppstyle(examples ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_c/*.c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_pmemobj_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_config_c/*.c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_open_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_comparator_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_comparator_c/*.c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_transaction_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_transaction_c/*.c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_iterator_c/*.c ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_iterator_cpp/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_fill_cpp/*.cpp) add_check_whitespace(examples ${CMAKE_CURRENT_SOURCE_DIR}/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_c/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_basic_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_pmemobj_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_config_c/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_open_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_comparator_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_comparator_c/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_transaction_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_transaction_c/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_iterator_c/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_iterator_cpp/*.* ${CMAKE_CURRENT_SOURCE_DIR}/pmemkv_fill_cpp/*.*) function(add_example name) set(srcs ${ARGN}) prepend(srcs ${CMAKE_CURRENT_SOURCE_DIR} ${srcs}) add_executable(example-${name} ${srcs}) add_dependencies(examples example-${name}) endfunction() # ----------------------------------------------------------------- # ## Add examples (if engine used in specific example is enabled) # ----------------------------------------------------------------- # if(ENGINE_CMAP) 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) add_example(pmemkv_pmemobj_cpp pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp) target_link_libraries(example-pmemkv_pmemobj_cpp pmemkv ${LIBPMEMOBJ++_LIBRARIES}) add_example(pmemkv_basic_config_c pmemkv_config_c/pmemkv_basic_config.c) target_link_libraries(example-pmemkv_basic_config_c pmemkv ${LIBPMEMOBJ++_LIBRARIES}) 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() add_example(pmemkv_open_cpp pmemkv_open_cpp/pmemkv_open.cpp) target_link_libraries(example-pmemkv_open_cpp pmemkv) else() message(STATUS "Some examples use cmap engine, which is disabled, hence they are " "not built. If you want to build them use -DENGINE_CMAP=ON option.") endif() if(ENGINE_CSMAP) add_example(pmemkv_comparator_cpp pmemkv_comparator_cpp/pmemkv_comparator.cpp) target_link_libraries(example-pmemkv_comparator_cpp pmemkv) add_example(pmemkv_comparator_c pmemkv_comparator_c/pmemkv_comparator.c) target_link_libraries(example-pmemkv_comparator_c pmemkv) include(FindThreads) if (CMAKE_USE_PTHREADS_INIT) add_example(pmemkv_iterator_cpp pmemkv_iterator_cpp/pmemkv_iterator.cpp) target_link_libraries(example-pmemkv_iterator_cpp pmemkv pthread) endif() else() message(STATUS "Some examples use csmap engine, which is disabled, hence they are " "not built. If you want to build them use -DENGINE_CSMAP=ON option.") endif() if(ENGINE_RADIX) add_example(pmemkv_transaction_cpp pmemkv_transaction_cpp/pmemkv_transaction.cpp) target_link_libraries(example-pmemkv_transaction_cpp pmemkv ${LIBPMEMOBJ++_LIBRARIES}) add_example(pmemkv_transaction_c pmemkv_transaction_c/pmemkv_transaction.c) target_link_libraries(example-pmemkv_transaction_c pmemkv ${LIBPMEMOBJ++_LIBRARIES}) add_example(pmemkv_iterator_c pmemkv_iterator_c/pmemkv_iterator.c) target_link_libraries(example-pmemkv_iterator_c pmemkv) else() message(STATUS "Some examples use radix engine, which is disabled, hence they are " "not built. If you want to build them use -DENGINE_RADIX=ON option.") endif() if(NOT ENGINE_CMAP AND NOT ENGINE_CSMAP AND NOT ENGINE_RADIX) message(WARNING "None of engines required for examples was enabled, " "so no examples were built. See messages above for instructions.") endif() # Engine is this example can be paremetrized at runtime add_example(pmemkv_fill_cpp pmemkv_fill_cpp/pmemkv_fill.cpp) target_link_libraries(example-pmemkv_fill_cpp pmemkv) pmemkv-1.5.0/examples/README.md000066400000000000000000000057161410000423300161050ustar00rootroot00000000000000# Examples This directory contains C and C++ examples for pmemkv, the library providing key-value datastore optimized for persistent memory. For more information about library see libpmemkv(3). ## Building and execution ### Build with sources To compile examples with current pmemkv sources, follow build steps for your OS (as described in top-level README). Make sure the BUILD_EXAMPLES options is ON. The build directory should now contain `examples` sub-directory with all binaries, ready to run, e.g.: ```sh cd /examples ./example-pmemkv_basic_c ``` If an example requires additional parameter it will print its usage, otherwise it will run and print the execution results (if any). ### Standalone build To compile any example as a standalone application (using pmemkv installed in the OS) you have to enter selected example's sub-directory and run e.g.: ```sh cd /examples/pmemkv_basic_c mkdir build cd build cmake .. make ./pmemkv_basic_c ``` Similarily to previous section, if an example requires additional parameter it will print its usage, otherwise it will run and print the execution results (if any). ## Descriptions and additional dependencies: * 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_comparator_c/pmemkv_comparator.c -- example of pmemkv used with custom comparator (C API) * pmemkv_comparator_c/pmemkv_comparator.cpp -- example of pmemkv used with custom comparator (C++ API) * pmemkv_config_c/ * pmemkv_basic_config.c -- example usage of the part of the pmemkv config API, which should be preferred. * pmemkv_config.c -- example usage of the part of the pmemkv config API to set and get data based on their data types. It **requires** to be built: * 'rapidjson-devel' package to be installed in the OS and * 'BUILD_JSON_CONFIG' pmemkv's CMake variable to be set to ON * pmemkv_fill_cpp/pmemkv_fill.cpp -- example which calculates how many elements fit into pmemkv. It inserts elements with specified key and value size to the database until OUT_OF_MEMORY status is returned. It then prints number of elements inserted. It may be used to observe the memory overhead of a certain engine with specific key/value sizes. * pmemkv_iterator_c/pmemkv_iterator.c -- example of pmemkv's iterator * pmemkv_iterator_cpp/pmemkv_iterator.cpp -- example of pmemkv's iterators. It shows how to use it in single-threaded and concurrent approach. It **requires** to be built: * pthread available in the OS * pmemkv_open_cpp/pmemkv_open_cpp -- contains example of pmemkv usage for already existing pools (and poolsets) * pmemkv_pmemobj_cpp/pmemkv_pmemobj_basic.cpp -- contains example of pmemkv supporting multiple engines * pmemkv_transaction_c/pmemkv_transaction.c -- example with pmemkv transactions (C API) * pmemkv_transaction_cpp/pmemkv_transaction.cpp -- example with pmemkv transactions (C++ API) pmemkv-1.5.0/examples/example.poolset000066400000000000000000000001341410000423300176550ustar00rootroot00000000000000PMEMPOOLSET 100M /dev/shm/pmemkv_open_ex_pool.part0 100M /dev/shm/pmemkv_open_ex_pool.part1 pmemkv-1.5.0/examples/pmemkv_basic_c/000077500000000000000000000000001410000423300175575ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_basic_c/CMakeLists.txt000066400000000000000000000006471410000423300223260ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_basic C) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) 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.5.0/examples/pmemkv_basic_c/pmemkv_basic.c000066400000000000000000000056121410000423300223670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_basic.c -- example usage of pmemkv. */ #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) #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_path(cfg, argv[1]); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_size(cfg, SIZE); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_create_if_missing(cfg, true); ASSERT(s == PMEMKV_STATUS_OK); /* Alternatively create_or_error_if_exists flag can be set, to fail if file exists * For differences between the two flags, see manpage libpmemkv(7). */ /* s = pmemkv_config_put_create_or_error_if_exists(cfg, true); */ 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.5.0/examples/pmemkv_basic_cpp/000077500000000000000000000000001410000423300201175ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_basic_cpp/CMakeLists.txt000066400000000000000000000006571410000423300226670ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_basic CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) 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.5.0/examples/pmemkv_basic_cpp/pmemkv_basic.cpp000066400000000000000000000050771410000423300232740ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_basic.cpp -- example of basic pmemkv usage. */ //! [basic] #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #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_path(argv[1]); ASSERT(s == status::OK); s = cfg.put_size(SIZE); ASSERT(s == status::OK); s = cfg.put_create_if_missing(true); ASSERT(s == status::OK); /* Alternatively create_or_error_if_exists flag can be set, to fail if file exists * For differences between the two flags, see e.g. libpmemkv(7) manpage. */ /* s = cfg.put_create_or_error_if_exists(true); */ LOG("Opening pmemkv database with 'cmap' engine"); db kv; 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"); s = kv.put("key2", "value2"); ASSERT(s == status::OK); s = kv.put("key3", "value3"); ASSERT(s == status::OK); 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); /* Examples of using pmem:kv:status with std::ostream and operator<<, * it's useful for debugging. */ /* Print status */ std::cout << s << std::endl; /* Write status to ostringstream */ std::ostringstream oss; oss << s; assert(oss.str() == "NOT_FOUND (2)"); LOG("Closing database"); return 0; } //! [basic] pmemkv-1.5.0/examples/pmemkv_comparator_c/000077500000000000000000000000001410000423300206455ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_comparator_c/CMakeLists.txt000066400000000000000000000006611410000423300234100ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_basic C) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_comparator_c pmemkv_comparator.c) target_link_libraries(pmemkv_comparator_c ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_comparator_c/pmemkv_comparator.c000066400000000000000000000055641410000423300245510ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_comparator.c -- example usage of pmemkv with custom comparator. */ #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define LOG(msg) puts(msg) 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 reverse_three_way_compare(const char *key1, size_t keybytes1, const char *key2, size_t keybytes2, void *arg) { size_t m_l = MIN(keybytes1, keybytes2); int r = memcmp(key2, key1, m_l); if (r == 0) { if (keybytes2 < keybytes1) return -1; else if (keybytes2 > keybytes1) return 1; else return 0; } return r; } 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_path(cfg, argv[1]); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_size(cfg, SIZE); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_create_if_missing(cfg, true); ASSERT(s == PMEMKV_STATUS_OK); pmemkv_comparator *cmp = pmemkv_comparator_new(&reverse_three_way_compare, "reverse_three_way_compare", NULL); ASSERT(cmp != NULL); s = pmemkv_config_put_comparator(cfg, cmp); ASSERT(s == PMEMKV_STATUS_OK); LOG("Opening pmemkv database with 'csmap' engine"); pmemkv_db *db = NULL; s = pmemkv_open("csmap", cfg, &db); ASSERT(s == PMEMKV_STATUS_OK); ASSERT(db != NULL); LOG("Putting new keys"); const char *key1 = "key1"; const char *value1 = "value1"; const char *key2 = "key2"; const char *value2 = "value2"; const char *key3 = "key3"; const char *value3 = "value3"; s = pmemkv_put(db, key1, strlen(key1), value1, strlen(value1)); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_put(db, key2, strlen(key2), value2, strlen(value2)); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_put(db, key3, strlen(key3), value3, strlen(value3)); ASSERT(s == PMEMKV_STATUS_OK); LOG("Iterating over existing keys in order specified by the comparator"); s = pmemkv_get_all(db, &get_kv_callback, NULL); ASSERT(s == PMEMKV_STATUS_OK); LOG("Closing database"); pmemkv_close(db); return 0; } pmemkv-1.5.0/examples/pmemkv_comparator_cpp/000077500000000000000000000000001410000423300212055ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_comparator_cpp/CMakeLists.txt000066400000000000000000000006711410000423300237510ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_basic CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_comparator_cpp pmemkv_comparator.cpp) target_link_libraries(pmemkv_comparator_cpp ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_comparator_cpp/pmemkv_comparator.cpp000066400000000000000000000044561410000423300254500ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_comparator.cpp -- example usage of pmemkv with custom comparator. */ #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; const uint64_t SIZE = 1024UL * 1024UL * 1024UL; //! [custom-comparator] class lexicographical_comparator { public: int compare(string_view k1, string_view k2) { if (k1.compare(k2) == 0) return 0; else if (std::lexicographical_compare(k1.data(), k1.data() + k1.size(), k2.data(), k2.data() + k2.size())) return -1; else return 1; } std::string name() { return "lexicographical_comparator"; } }; //! [custom-comparator] 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"); //! [comparator-usage] config cfg; status s = cfg.put_path(argv[1]); ASSERT(s == status::OK); s = cfg.put_size(SIZE); ASSERT(s == status::OK); s = cfg.put_create_if_missing(true); ASSERT(s == status::OK); s = cfg.put_comparator(lexicographical_comparator{}); ASSERT(s == status::OK); LOG("Opening pmemkv database with 'csmap' engine"); db *kv = new db(); ASSERT(kv != nullptr); s = kv->open("csmap", std::move(cfg)); ASSERT(s == status::OK); LOG("Putting new keys"); s = kv->put("key1", "value1"); ASSERT(s == status::OK); s = kv->put("key2", "value2"); ASSERT(s == status::OK); s = kv->put("key3", "value3"); ASSERT(s == status::OK); LOG("Iterating over existing keys in order specified by the comparator"); kv->get_all([](string_view k, string_view v) { LOG(" visited: " << k.data()); return 0; }); //! [comparator-usage] LOG("Closing database"); delete kv; return 0; } pmemkv-1.5.0/examples/pmemkv_config_c/000077500000000000000000000000001410000423300177435ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_config_c/CMakeLists.txt000066400000000000000000000007011410000423300225010ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_config C) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv libpmemkv_json_config) 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.5.0/examples/pmemkv_config_c/pmemkv_basic_config.c000066400000000000000000000047411410000423300241020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_basic_config.c -- example usage of the part of the pmemkv config API, * which should be preferred. */ #include #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) static const uint64_t SIZE = 1024UL * 1024UL * 1024UL; int key_length_compare(const char *key1, size_t keybytes1, const char *key2, size_t keybytes2, void *arg) { if (keybytes2 < keybytes1) return -1; else if (keybytes2 > keybytes1) return 1; else return 0; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s file\n", argv[0]); exit(1); } /* Create config */ pmemkv_config *config = pmemkv_config_new(); ASSERT(config != NULL); /* Add path parameter to config. Meaning of this is dependent on chosen engine. * E.g. if config is used with cmap engine, * it is a path to a database file or to a poolset file. However for * vcmap it is a path to an existing directory */ int status = pmemkv_config_put_path(config, argv[1]); ASSERT(status == PMEMKV_STATUS_OK); /* Specifies size of the database */ status = pmemkv_config_put_size(config, SIZE); ASSERT(status == PMEMKV_STATUS_OK); /* Specifies value of create_if_missing flag. * Alternatively, another flag - 'create_or_error_if_exists' can be set using: * `pmemkv_config_put_create_or_error_if_exists` * For differences between the two, see manpage libpmemkv(7). */ status = pmemkv_config_put_create_if_missing(config, true); ASSERT(status == PMEMKV_STATUS_OK); /* Specifies comparator used by the engine */ pmemkv_comparator *cmp = pmemkv_comparator_new(&key_length_compare, "key_length_compare", NULL); ASSERT(cmp != NULL); status = pmemkv_config_put_comparator(config, cmp); ASSERT(status == PMEMKV_STATUS_OK); /* Adds pointer to oid (for details see libpmemkv(7)) to the config */ PMEMoid oid; status = pmemkv_config_put_oid(config, &oid); ASSERT(status == PMEMKV_STATUS_OK); pmemkv_config_delete(config); return 0; } pmemkv-1.5.0/examples/pmemkv_config_c/pmemkv_config.c000066400000000000000000000056451410000423300227450ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_config.c -- example usage of the part of the pmemkv config API * to set and get data based on their data types. */ #include #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) /* 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.5.0/examples/pmemkv_fill_cpp/000077500000000000000000000000001410000423300177645ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_fill_cpp/CMakeLists.txt000066400000000000000000000006471410000423300225330ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2021, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_basic CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_fill_cpp pmemkv_fill.cpp) target_link_libraries(pmemkv_fill_cpp ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_fill_cpp/pmemkv_fill.cpp000066400000000000000000000052661410000423300230060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ /* * pmemkv_fill.cpp -- example which calculates how many elements fit * into pmemkv. It inserts elements with specified key and value size * to the database until OUT_OF_MEMORY status is returned. It then prints * number of elements inserted. It may be used to observe the memory overhead * of a certain engine with specific key/value sizes. */ #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; size_t insert_till_oom(db *kv, size_t key_size, size_t value_size) { size_t num = 0; std::string v(value_size, 'x'); status s; do { if (num % 100000 == 0) std::cout << "Inserting " << num << "th key..." << std::endl; std::string k((char *)&num, sizeof(uint64_t)); if (k.size() < key_size) k += std::string(key_size - k.size(), 'x'); s = kv->put(k, v); num++; } while (s == status::OK); ASSERT(s == status::OUT_OF_MEMORY); return num; } int main(int argc, char *argv[]) { if (argc < 6) { std::cerr << "Usage: " << argv[0] << " file size engine key_size value_size" << std::endl; exit(1); } std::string path = argv[1]; size_t size = std::stoull(std::string(argv[2])); std::string engine = argv[3]; size_t key_size = std::stoull(std::string(argv[4])); size_t value_size = std::stoull(std::string(argv[5])); if (key_size < 8) { std::cerr << "Key size must be at least 8 bytes" << std::endl; exit(1); } /* See libpmemkv_config(3) for more detailed example of config creation */ config cfg; status s = cfg.put_path(path); ASSERT(s == status::OK); s = cfg.put_size(size); ASSERT(s == status::OK); s = cfg.put_create_if_missing(true); ASSERT(s == status::OK); /* Alternatively create_or_error_if_exists flag can be set, to fail if file exists * For differences between the two flags, see e.g. libpmemkv(7) manpage. */ /* s = cfg.put_create_or_error_if_exists(true); */ db *kv = new db(); ASSERT(kv != nullptr); s = kv->open(engine, std::move(cfg)); ASSERT(s == status::OK); auto elements = insert_till_oom(kv, key_size, value_size); std::cout << "Number of elements: " << elements << std::endl; LOG("Closing database"); delete kv; return 0; } pmemkv-1.5.0/examples/pmemkv_iterator_c/000077500000000000000000000000001410000423300203275ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_iterator_c/CMakeLists.txt000066400000000000000000000006601410000423300230710ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_iterator_c C) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_iterator_c pmemkv_iterator.c) target_link_libraries(pmemkv_iterator_c ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_iterator_c/pmemkv_iterator.c000066400000000000000000000065641410000423300237160ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_iterator.c -- example usage of pmemkv's iterator. */ #include "libpmemkv.h" #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) #define LOG(msg) puts(msg) static const uint64_t SIZE = 1024UL * 1024UL * 1024UL; int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s file\n", argv[0]); exit(1); } const size_t n_elements = 10; char buffer[64]; /* 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_path(cfg, argv[1]); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_size(cfg, SIZE); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_config_put_create_if_missing(cfg, true); ASSERT(s == PMEMKV_STATUS_OK); LOG("Opening pmemkv database with 'radix' engine"); pmemkv_db *db = NULL; s = pmemkv_open("radix", cfg, &db); ASSERT(s == PMEMKV_STATUS_OK); ASSERT(db != NULL); LOG("Putting new keys"); for (size_t i = 0; i < n_elements; ++i) { char key[10]; const char *value = "value"; sprintf(key, "key%zu", i); s = pmemkv_put(db, key, strlen(key), value, strlen(value)); ASSERT(s == PMEMKV_STATUS_OK); } /* get a new read iterator */ pmemkv_iterator *it; s = pmemkv_iterator_new(db, &it); ASSERT(s == PMEMKV_STATUS_OK); LOG("Iterate from first to last element"); s = pmemkv_iterator_seek_to_first(it); ASSERT(s == PMEMKV_STATUS_OK); size_t element_number = 0; do { const char *str; size_t cnt; /* read a key */ s = pmemkv_iterator_key(it, &str, &cnt); ASSERT(s == PMEMKV_STATUS_OK); sprintf(buffer, "Key %zu = %s", element_number++, str); LOG(buffer); } while (pmemkv_iterator_next(it) != PMEMKV_STATUS_NOT_FOUND); /* iterator must be deleted manually */ pmemkv_iterator_delete(it); /* get a new write_iterator */ pmemkv_write_iterator *w_it; s = pmemkv_write_iterator_new(db, &w_it); ASSERT(s == PMEMKV_STATUS_OK); /* if you want to get a pmemkv_iterator (read iterator) from a * pmemkv_write_iterator, you should do: write_it->iter */ s = pmemkv_iterator_seek_to_last(w_it->iter); ASSERT(s == PMEMKV_STATUS_OK); /* get a write range, to modify last element's value */ char *data; size_t cnt; s = pmemkv_write_iterator_write_range(w_it, 0, 5, &data, &cnt); ASSERT(s == PMEMKV_STATUS_OK); for (size_t i = 0; i < cnt; ++i) data[i] = 'x'; /* commit changes */ s = pmemkv_write_iterator_commit(w_it); ASSERT(s == PMEMKV_STATUS_OK); /* get a read range, to read modified value */ const char *str; s = pmemkv_iterator_read_range(w_it->iter, 0, 5, &str, &cnt); ASSERT(s == PMEMKV_STATUS_OK); /* verify a modified value */ ASSERT(strcmp(str, "xxxxx") == 0); sprintf(buffer, "Modified value = %s", str); LOG(buffer); /* iterator must be deleted manually */ pmemkv_write_iterator_delete(w_it); LOG("Closing database"); pmemkv_close(db); return 0; } pmemkv-1.5.0/examples/pmemkv_iterator_cpp/000077500000000000000000000000001410000423300206675ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_iterator_cpp/CMakeLists.txt000066400000000000000000000007071410000423300234330ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_iterator_cpp CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_iterator_cpp pmemkv_iterator.cpp) target_link_libraries(pmemkv_iterator_cpp ${LIBPMEMKV_LIBRARIES} pthread) pmemkv-1.5.0/examples/pmemkv_iterator_cpp/pmemkv_iterator.cpp000066400000000000000000000160541410000423300246110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_iterator.cpp -- example of pmemkv's iterators. * Two usages, to show: single-threaded and concurrent approach. */ #include #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; const uint64_t SIZE = 1024UL * 1024UL * 1024UL; static std::unique_ptr init_kv(const std::string &engine, const std::string &path, size_t n_elements) { /* See libpmemkv_config(3) for more detailed example of config creation */ LOG("Creating a new config"); config cfg; status s = cfg.put_path(path); ASSERT(s == status::OK); s = cfg.put_size(SIZE); ASSERT(s == status::OK); s = cfg.put_create_if_missing(true); ASSERT(s == status::OK); LOG("Opening pmemkv database with " + engine + " engine"); auto kv = std::unique_ptr(new db()); ASSERT(kv != nullptr); s = kv->open(engine, std::move(cfg)); ASSERT(s == status::OK); LOG("Putting new keys"); for (size_t i = 0; i < n_elements; i++) { s = kv->put(std::to_string(i), std::string(10 + i, 'a')); ASSERT(s == status::OK); } return kv; } //! [single-threaded] static std::string read_key(pmem::kv::db::read_iterator &it) { /* key_result's type is a pmem::kv::result, for more information * check pmem::kv::result documentation */ pmem::kv::result key_result = it.key(); /* check if the result is ok, you can also do: * key_result == pmem::kv::status::OK * or * key_result.get_status() == pmem::kv::status::OK */ ASSERT(key_result.is_ok()); return key_result.get_value().data(); } static std::string read_value(pmem::kv::db::read_iterator &it) { /* val_result's type is a pmem::kv::result, for more information * check pmem::kv::result documentation */ pmem::kv::result val_result = it.read_range(); /* check if the result is ok, you can also do: * val_result == pmem::kv::status::OK * or * val_result.get_status() == pmem::kv::status::OK */ ASSERT(val_result.is_ok()); return val_result.get_value().data(); } static void single_threaded_engine_example(const std::string &path) { const size_t n_elements = 10; /* init radix engine */ auto kv = init_kv("radix", path + "_radix", n_elements); /* We shouldn't hold simultaneously in the same thread more than one iterator. So * every iterator in this example will be in a separate scope */ { /* get a new read iterator */ auto res_it = kv->new_read_iterator(); /* you should check if the result is ok before getting a value */ ASSERT(res_it.is_ok()); /* you need to take reference to the iterator from the result, because * iterator isn't copyable */ auto &it = res_it.get_value(); LOG("Iterate from first to last element"); auto s = it.seek_to_first(); size_t cnt = 0; ASSERT(s == status::OK); do { /* read a key */ auto key = read_key(it); ASSERT(key == std::to_string(cnt)); LOG("Key = " + key); /* read a value */ auto value = read_value(it); ASSERT(value == std::string(10 + cnt, 'a')); LOG("Value = " + value); ++cnt; } while (it.next() == status::OK); LOG("Iterate from last to first element"); s = it.seek_to_last(); cnt = n_elements - 1; ASSERT(s == status::OK); do { /* read a key */ auto key = read_key(it); ASSERT(key == std::to_string(cnt)); LOG("Key = " + key); /* read a value */ auto value = read_value(it); ASSERT(value == std::string(10 + cnt, 'a')); LOG("Value = " + value); --cnt; } while (it.prev() == status::OK); /* read iterator is being destroyed now */ } /* scope for a write iterator */ { /* get a new write iterator */ auto res_w_it = kv->new_write_iterator(); /* you should check if the result is ok before getting a value */ ASSERT(res_w_it.is_ok()); /* you need to take reference to the iterator from the result, because * iterator isn't copyable */ auto &w_it = res_w_it.get_value(); LOG("Modify value of the elements lower than \"5\""); /* seek to the element lower than "5" */ status s = w_it.seek_lower("5"); ASSERT(s == status::OK); do { /* read a value before writing */ std::string value_before_write = w_it.read_range().get_value().data(); /* Get a write range. By default it is a whole value (pos = 0, n = * std::numeric_limits::max()). */ auto res = w_it.write_range(); ASSERT(res.is_ok()); /* set all chars to 'x' */ for (auto &c : res.get_value()) { /* note that you can't read elements from write_range, so * e.g. char x = c isn't possible. If you want to read a * value, you need to use read_range method instead of * write_range */ c = 'x'; } /* commit modifications */ w_it.commit(); pmem::kv::result key_result = w_it.key(); ASSERT(key_result.is_ok()); std::string current_key = key_result.get_value().data(); LOG("Key = " + current_key); std::string current_value = w_it.read_range().get_value().data(); /* check if the value has changed */ ASSERT(current_value.compare(value_before_write) != 0); ASSERT(current_value.compare( std::string(current_value.size(), 'x')) == 0); LOG("Value after commit = " + current_value); } while (w_it.prev() == status::OK); /* write iterator is being destroyed now */ } } //! [single-threaded] //! [concurrent] static void concurrent_engine_example(const std::string &path) { const size_t n_elements = 20; /* init csmap engine */ auto kv = init_kv("csmap", path + "_csmap", n_elements); /* create 2 threads, in first thread iterate from the beginning to the element * with key equal "5", in second from element with key equal "5" to the end */ std::vector threads; /* thread1 */ threads.emplace_back([&]() { auto res_it = kv->new_read_iterator(); ASSERT(res_it.is_ok()); auto &it = res_it.get_value(); it.seek_to_first(); do { /* read a key */ auto key = read_key(it); LOG("Key (from thread1) = " + key); } while (it.next() == pmem::kv::status::OK && read_key(it).compare("5")); }); /* thread2 */ threads.emplace_back([&]() { auto res_it = kv->new_read_iterator(); ASSERT(res_it.is_ok()); auto &it = res_it.get_value(); it.seek("5"); do { /* read a key */ auto key = read_key(it); LOG("Key (from thread2) = " + key); } while (it.next() == pmem::kv::status::OK); }); for (auto &th : threads) th.join(); } //! [concurrent] int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " file" << std::endl; exit(1); } single_threaded_engine_example(argv[1]); concurrent_engine_example(argv[1]); return 0; } pmemkv-1.5.0/examples/pmemkv_open_cpp/000077500000000000000000000000001410000423300177775ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_open_cpp/CMakeLists.txt000066400000000000000000000006571410000423300225470ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_open_cpp CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) 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.5.0/examples/pmemkv_open_cpp/pmemkv_open.cpp000066400000000000000000000045551410000423300230340ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_open.cpp -- example usage of pmemkv with already existing pools. */ #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; //! [open] /* * This example expects a path to already created database pool. * * If you want to create 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; /* Instead of expecting already created database pool, we could simply * set 'create_if_missing' flag in the config, to provide a pool if needed. */ status s = cfg.put_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"); s = kv->put("key2", "value2"); ASSERT(s == status::OK); s = kv->put("key3", "value3"); ASSERT(s == status::OK); 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; } //! [open] pmemkv-1.5.0/examples/pmemkv_pmemobj_cpp/000077500000000000000000000000001410000423300204675ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_pmemobj_cpp/CMakeLists.txt000066400000000000000000000011251410000423300232260ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_pmemobj CXX) set(LIBPMEMOBJ_CPP_REQUIRED_VERSION 1.9) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++>=${LIBPMEMOBJ_CPP_REQUIRED_VERSION}) 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.5.0/examples/pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp000066400000000000000000000065231410000423300242110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* * pmemkv_pmemobj.cpp -- example usage of pmemkv * supporting multiple engines. */ //! [multiple-engines] #include #include #include #include #include #include #include #include #include #undef LOG #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #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_oid(&(pop.root()->oids->at(0))); ASSERT(ret == status::OK); ret = cfg_2.put_oid(&(pop.root()->oids->at(1))); 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 << "Exception occurred: " << e.what() << std::endl; } try { pop.close(); } catch (const std::logic_error &e) { std::cerr << "Exception occurred: " << e.what() << std::endl; } return 0; } //! [multiple-engines] pmemkv-1.5.0/examples/pmemkv_transaction_c/000077500000000000000000000000001410000423300210235ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_transaction_c/CMakeLists.txt000066400000000000000000000006741410000423300235720ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_transaction_c C) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_transaction_c pmemkv_transaction.c) target_link_libraries(pmemkv_transaction_c ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_transaction_c/pmemkv_transaction.c000066400000000000000000000055301410000423300250760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_transaction.c -- example usage of pmemkv transactions. */ #include #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ puts(pmemkv_errormsg()); \ assert(expr); \ } while (0) #define LOG(msg) puts(msg) /* * This example expects a path to already created database pool. * * To create a pool use one of the following commands. * * For regular pools use: * pmempool create -l -s 1G "pmemkv_radix" obj path_to_a_pool * * For poolsets use: * pmempool create -l "pmemkv_radix" obj ../examples/example.poolset */ int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: %s pool\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_path(cfg, argv[1]); ASSERT(s == PMEMKV_STATUS_OK); LOG("Opening pmemkv database with 'radix' engine"); pmemkv_db *db = NULL; s = pmemkv_open("radix", cfg, &db); ASSERT(s == PMEMKV_STATUS_OK); ASSERT(db != NULL); const char *key1 = "key1"; const char *value1 = "value1"; const char *key2 = "key2"; const char *value2 = "value2"; const char *key3 = "key3"; const char *value3 = "value3"; LOG("Putting new key"); s = pmemkv_put(db, key1, strlen(key1), value1, strlen(value1)); ASSERT(s == PMEMKV_STATUS_OK); LOG("Starting a tx"); pmemkv_tx *tx; s = pmemkv_tx_begin(db, &tx); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_tx_remove(tx, key1, strlen(key1)); s = pmemkv_tx_put(tx, key2, strlen(key2), value2, strlen(value2)); s = pmemkv_tx_put(tx, key3, strlen(key3), value3, strlen(value3)); /* Until transaction is committed, changes are not visible */ s = pmemkv_exists(db, key1, strlen(key1)); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_exists(db, key2, strlen(key2)); ASSERT(s == PMEMKV_STATUS_NOT_FOUND); s = pmemkv_exists(db, key3, strlen(key3)); ASSERT(s == PMEMKV_STATUS_NOT_FOUND); s = pmemkv_tx_commit(tx); ASSERT(s == PMEMKV_STATUS_OK); /* Changes are now visible and pmemkv_tx object is destroyed */ s = pmemkv_exists(db, key1, strlen(key1)); ASSERT(s == PMEMKV_STATUS_NOT_FOUND); s = pmemkv_exists(db, key2, strlen(key2)); ASSERT(s == PMEMKV_STATUS_OK); s = pmemkv_exists(db, key3, strlen(key3)); ASSERT(s == PMEMKV_STATUS_OK); LOG("Ending transaction"); pmemkv_tx_end(tx); LOG("Closing database"); pmemkv_close(db); return 0; } pmemkv-1.5.0/examples/pmemkv_transaction_cpp/000077500000000000000000000000001410000423300213635ustar00rootroot00000000000000pmemkv-1.5.0/examples/pmemkv_transaction_cpp/CMakeLists.txt000066400000000000000000000007061410000423300241260ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation cmake_minimum_required(VERSION 3.3) project(pmemkv_transaction_cpp CXX) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBPMEMKV REQUIRED libpmemkv) include_directories(${LIBPMEMKV_INCLUDE_DIRS}) link_directories(${LIBPMEMKV_LIBRARY_DIRS}) add_executable(pmemkv_transaction_cpp pmemkv_transaction.cpp) target_link_libraries(pmemkv_transaction_cpp ${LIBPMEMKV_LIBRARIES}) pmemkv-1.5.0/examples/pmemkv_transaction_cpp/pmemkv_transaction.cpp000066400000000000000000000050651410000423300260010ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * pmemkv_transaction.cpp -- example usage of pmemkv transactions. */ #include #include #include #include #define ASSERT(expr) \ do { \ if (!(expr)) \ std::cout << pmemkv_errormsg() << std::endl; \ assert(expr); \ } while (0) #define LOG(msg) std::cout << msg << std::endl using namespace pmem::kv; /* * This example expects a path to already created database pool. * * To create a pool use one of the following commands. * * For regular pools use: * pmempool create -l -s 1G "pmemkv_radix" obj path_to_a_pool * * For poolsets use: * pmempool create -l "pmemkv_radix" obj ../examples/example.poolset */ //! [transaction] 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_path(argv[1]); ASSERT(s == status::OK); LOG("Opening pmemkv database with 'radix' engine"); db kv; s = kv.open("radix", std::move(cfg)); ASSERT(s == status::OK); LOG("Putting new key"); s = kv.put("key1", "value1"); ASSERT(s == status::OK); auto result_tx = kv.tx_begin(); ASSERT(result_tx.is_ok()); /* This function is guaranteed to not throw if is_ok is true */ auto &tx = result_tx.get_value(); s = tx.remove("key1"); s = tx.put("key2", "value2"); s = tx.put("key3", "value3"); /* Until transaction is committed, changes are not visible */ ASSERT(kv.exists("key1") == status::OK); ASSERT(kv.exists("key2") == status::NOT_FOUND); ASSERT(kv.exists("key3") == status::NOT_FOUND); s = tx.commit(); ASSERT(s == status::OK); ASSERT(kv.exists("key1") == status::NOT_FOUND); ASSERT(kv.exists("key2") == status::OK); ASSERT(kv.exists("key3") == status::OK); { /* Alternative method of obtaining tx object. This line can throw if * tx_begin() fails. */ auto tx = kv.tx_begin().get_value(); s = tx.put("key4", "value4"); s = tx.put("key5", "value5"); } /* The second tx was not committed, so the changes are not visible. */ ASSERT(kv.exists("key4") == status::NOT_FOUND); ASSERT(kv.exists("key5") == status::NOT_FOUND); return 0; } //! [transaction] pmemkv-1.5.0/libpmemkv.pc.in000066400000000000000000000006111410000423300157140ustar00rootroot00000000000000version=@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.5.0/libpmemkv_json_config.pc.in000066400000000000000000000006451410000423300203010ustar00rootroot00000000000000version=@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.5.0/src/000077500000000000000000000000001410000423300135665ustar00rootroot00000000000000pmemkv-1.5.0/src/README.md000066400000000000000000000017171410000423300150530ustar00rootroot00000000000000pmemkv {#mainpage} =========================== Key/Value Datastore for Persistent Memory All general information about (lib)pmemkv can be found on the webstie: https://pmem.io/pmemkv Main code repository location: https://github.com/pmem/pmemkv (it contains full sources of all examples' snippets from this documentation) 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 * class pmem::kv::tx for grouping operations into a single atomic action * class pmem::kv::db::iterator to iterate over records in db * enum class pmem::kv::status containing all possible statuses returned by most of public functions pmemkv-1.5.0/src/comparator/000077500000000000000000000000001410000423300157355ustar00rootroot00000000000000pmemkv-1.5.0/src/comparator/comparator.h000066400000000000000000000032501410000423300202550ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #ifndef LIBPMEMKV_COMPARATOR_H #define LIBPMEMKV_COMPARATOR_H #include "../config.h" #include "../exceptions.h" #include "../libpmemkv.h" #include "../libpmemkv.hpp" #include "../out.h" namespace pmem { namespace kv { namespace internal { class comparator { public: comparator(pmemkv_compare_function *cmp, std::string name, void *arg) : cmp(cmp), name_(name), arg(arg) { } int compare(string_view key1, string_view key2) const { return (*cmp)(key1.data(), key1.size(), key2.data(), key2.size(), arg); } std::string name() const { return name_; } private: pmemkv_compare_function *cmp; std::string name_; void *arg; }; static inline int binary_compare(const char *key1, size_t kb1, const char *key2, size_t kb2, void *arg) { (void)arg; return string_view(key1, kb1).compare(string_view(key2, kb2)); } static inline const comparator &binary_comparator() { static const comparator cmp(binary_compare, "__pmemkv_binary_comparator", nullptr); return cmp; } template ().c_str())>::value>::type> static inline string_view make_string_view(const T &str) { return string_view(str.c_str(), str.size()); } static inline string_view make_string_view(string_view v) { return v; } static inline const comparator *extract_comparator(internal::config &cfg) { comparator *cmp; if (!cfg.get_object("comparator", (void **)&cmp)) return &internal::binary_comparator(); else return cmp; } } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif pmemkv-1.5.0/src/comparator/pmemobj_comparator.h000066400000000000000000000031341410000423300217670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #ifndef LIBPMEMKV_PMEMOBJ_COMPARATOR_H #define LIBPMEMKV_PMEMOBJ_COMPARATOR_H #include "../exceptions.h" #include "comparator.h" #include "../valgrind/pmemcheck.h" #include namespace pmem { namespace kv { namespace internal { class pmemobj_compare { public: using is_transparent = void; pmemobj_compare() : name() { } void initialize(const comparator *cmp) { /* initialize should be called only once */ assert(this->name.size() == 0); if (cmp->name().size() == 0) throw internal::invalid_argument( "Comparator does not have a valid name"); this->cmp = cmp; auto pop = pmem::obj::pool_base(pmemobj_pool_by_ptr(this)); pop.persist(&this->cmp, sizeof(this->cmp)); this->name = cmp->name(); } void runtime_initialize(const comparator *cmp) { if (this->name != cmp->name()) throw internal::comparator_mismatch( "Comparator with name: \"" + std::string(this->name.c_str()) + "\" expected"); this->cmp = cmp; auto pop = pmem::obj::pool_base(pmemobj_pool_by_ptr(this)); pop.persist(&this->cmp, sizeof(this->cmp)); } template bool operator()(const T &lhs, const U &rhs) const { auto key1 = make_string_view(lhs); auto key2 = make_string_view(rhs); return (cmp->compare(key1, key2) < 0); } private: pmem::obj::string name; const comparator *cmp = nullptr; }; static_assert(sizeof(pmemobj_compare) == 40, "wrong pmemobj_compare size"); } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif pmemkv-1.5.0/src/comparator/volatile_comparator.h000066400000000000000000000013151410000423300221540ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #ifndef LIBPMEMKV_VOLATILE_COMPARATOR_H #define LIBPMEMKV_VOLATILE_COMPARATOR_H #include "../exceptions.h" #include "comparator.h" namespace pmem { namespace kv { namespace internal { class volatile_compare { public: using is_transparent = void; volatile_compare(const comparator *cmp) : cmp(cmp) { } template bool operator()(const T &lhs, const U &rhs) const { auto key1 = make_string_view(lhs); auto key2 = make_string_view(rhs); return (cmp->compare(key1, key2) < 0); } private: const comparator *cmp; }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif pmemkv-1.5.0/src/config.h000066400000000000000000000151141410000423300152060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #ifndef LIBPMEMKV_CONFIG_H #define LIBPMEMKV_CONFIG_H #include #include #include #include #include "exceptions.h" #include "libpmemkv.hpp" namespace pmem { namespace kv { namespace internal { class 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 *), void *(*getter)(void *) = [](void *arg) { return arg; }) { put(key, value, deleter, getter); } 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); } /* * @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 'data' */ bool get_data(const char *key, const void **value, size_t *value_size) { auto item = get_checked_item(key, type::DATA); if (!item) return false; *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_checked_item(key, type::OBJECT); if (item == nullptr) return false; *value = (*item->object.getter)(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, type::INT64); 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, type::UINT64); 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_checked_item(key, type::STRING); if (item == nullptr) return false; *value = item->string_v.c_str(); return true; } /* * Returns value for path property from config. * * @throw pmem::kv::internal::invalid_argument if item does not exist */ std::string get_path() { const char *path; if (!get_string("path", &path)) throw internal::invalid_argument( "Config does not contain item with key: \"path\""); return std::string(path); } /* * Returns value for size property from config. * * @throw pmem::kv::internal::invalid_argument if item does not exist */ uint64_t get_size() { std::size_t size; if (!get_uint64("size", &size)) throw internal::invalid_argument( "Config does not contain item with key: \"size\""); return size; } 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 *), void *(*getter)(void *)) : object{object, deleter, getter}, 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 *); void *(*getter)(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: " + 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, type expected_type) { throw config_type_error( "Item with key: " + key + " is " + type_names[static_cast(item_type)] + ". Expected: " + type_names[static_cast(expected_type)]); } struct variant *get_checked_item(const char *key, type expected_type) { auto item = get(key); if (!item) return nullptr; if (item->item_type != expected_type) throw_type_error(key, item->item_type, expected_type); return item; } }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_CONFIG_H */ pmemkv-1.5.0/src/engine.cc000066400000000000000000000067731410000423300153570ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "engine.h" namespace pmem { namespace kv { std::map & storage_engine_factory::get_engine_factories() { static std::map factory_objects; return factory_objects; } bool storage_engine_factory::register_factory(factory_type factory) { auto factory_name = factory->get_name(); return get_engine_factories().emplace(factory_name, std::move(factory)).second; } std::unique_ptr storage_engine_factory::create_engine(const std::string &name, std::unique_ptr cfg) { auto &pairs = get_engine_factories(); auto it = pairs.find(name); if (it != pairs.end()) { return it->second->create(std::move(cfg)); } throw internal::wrong_engine_name("Unknown engine name \"" + name + "\". Available engines: " + get_names()); } std::string storage_engine_factory::get_names() { auto &factories = get_engine_factories(); if (factories.empty()) { return ""; } std::string separator = ", "; std::string result; for (auto &entry : get_engine_factories()) { result += entry.first + separator; } return result.erase(result.rfind(separator), separator.length()); } /* throws internal error (status) if config is null */ void 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"); } } 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; } internal::transaction *engine_base::begin_tx() { throw internal::not_supported("Transactions are not supported in this engine"); } engine_base::iterator *engine_base::new_iterator() { throw internal::not_supported("Iterators are not supported in this engine"); } engine_base::iterator *engine_base::new_const_iterator() { throw internal::not_supported("Iterators are not supported in this engine"); } } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engine.h000066400000000000000000000073251410000423300152130ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #ifndef LIBPMEMKV_ENGINE_H #define LIBPMEMKV_ENGINE_H #include #include #include #include "config.h" #include "iterator.h" #include "libpmemkv.hpp" #include "transaction.h" namespace pmem { namespace kv { void check_config_null(const std::string &engine_name, std::unique_ptr &cfg); /** * engine_base is a top-level interface for implementing new storage engine. * * All storage engines should implement its factory (engine_base::factory_base). * To activate new storage engine create object of factory_registerer and pass * factory instance. */ class engine_base { using iterator = internal::iterator_base; public: engine_base() = default; virtual ~engine_base() = default; 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); virtual internal::transaction *begin_tx(); virtual iterator *new_iterator(); virtual iterator *new_const_iterator(); /** * factory_base is an interface for engine factory. * Should be implemented for registration purposes. */ class factory_base { public: factory_base() = default; virtual ~factory_base() = default; virtual std::unique_ptr create(std::unique_ptr) = 0; virtual std::string get_name() = 0; }; }; /** * storage_engine_factory is a class for handling auto-registering factories. * Provides simple to use mechanism for factory registration without creating * dependencies for newly added engines. */ class storage_engine_factory { public: using factory_type = std::unique_ptr; storage_engine_factory() = delete; static bool register_factory(factory_type factory); static std::unique_ptr create_engine(const std::string &name, std::unique_ptr cfg); private: static std::map &get_engine_factories(); static std::string get_names(); }; /** * This class is intended to successfully initialize storage_engine_factory * despite optimization level due to [basic.stc.static]: * "If a variable with static storage duration has initialization or a destructor * with side effects, it shall not be eliminated even if it appears to be unused". */ class factory_registerer { public: factory_registerer() = delete; factory_registerer(storage_engine_factory::factory_type factory) { storage_engine_factory::register_factory(std::move(factory)); } }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_ENGINE_H */ pmemkv-1.5.0/src/engines-experimental/000077500000000000000000000000001410000423300177115ustar00rootroot00000000000000pmemkv-1.5.0/src/engines-experimental/csmap.cc000066400000000000000000000253511410000423300213310ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "csmap.h" #include "../iterator.h" #include "../out.h" namespace pmem { namespace kv { csmap::csmap(std::unique_ptr cfg) : pmemobj_engine_base(cfg, "pmemkv_csmap"), config(std::move(cfg)) { Recover(); LOG("Started ok"); } csmap::~csmap() { LOG("Stopped ok"); } std::string csmap::name() { return "csmap"; } status csmap::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); cnt = container->size(); return status::OK; } status csmap::count_above(string_view key, std::size_t &cnt) { LOG("count_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->upper_bound(key); auto last = container->end(); cnt = internal::distance(first, last); return status::OK; } status csmap::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_equal_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->lower_bound(key); auto last = container->end(); cnt = internal::distance(first, last); return status::OK; } status csmap::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_equal_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->begin(); auto last = container->upper_bound(key); cnt = internal::distance(first, last); return status::OK; } status csmap::count_below(string_view key, std::size_t &cnt) { LOG("count_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->begin(); auto last = container->lower_bound(key); cnt = internal::distance(first, last); return status::OK; } status csmap::count_between(string_view key1, string_view key2, std::size_t &cnt) { LOG("count_between for key1=" << key1.data() << ", key2=" << key2.data()); check_outside_tx(); if (container->key_comp()(key1, key2)) { shared_global_lock_type lock(mtx); auto first = container->upper_bound(key1); auto last = container->lower_bound(key2); cnt = internal::distance(first, last); } else { cnt = 0; } return status::OK; } status csmap::iterate(typename container_type::iterator first, typename container_type::iterator last, get_kv_callback *callback, void *arg) { for (auto it = first; it != last; ++it) { shared_node_lock_type lock(it->second.mtx); auto ret = callback(it->first.c_str(), it->first.size(), it->second.val.c_str(), it->second.val.size(), arg); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status csmap::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->begin(); auto last = container->end(); return iterate(first, last, callback, arg); } status csmap::get_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->upper_bound(key); auto last = container->end(); return iterate(first, last, callback, arg); } status csmap::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->lower_bound(key); auto last = container->end(); return iterate(first, last, callback, arg); } status csmap::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->begin(); auto last = container->upper_bound(key); return iterate(first, last, callback, arg); } status csmap::get_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto first = container->begin(); auto last = container->lower_bound(key); return iterate(first, last, callback, arg); } status csmap::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { LOG("get_between for key1=" << key1.data() << ", key2=" << key2.data()); check_outside_tx(); if (container->key_comp()(key1, key2)) { shared_global_lock_type lock(mtx); auto first = container->upper_bound(key1); auto last = container->lower_bound(key2); return iterate(first, last, callback, arg); } return status::OK; } status csmap::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); return container->contains(key) ? status::OK : status::NOT_FOUND; } status csmap::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); check_outside_tx(); shared_global_lock_type lock(mtx); auto it = container->find(key); if (it != container->end()) { shared_node_lock_type lock(it->second.mtx); callback(it->second.val.c_str(), it->second.val.size(), arg); return status::OK; } LOG(" key not found"); return status::NOT_FOUND; } status csmap::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(); shared_global_lock_type lock(mtx); auto result = container->try_emplace(key, value); if (result.second == false) { auto &it = result.first; unique_node_lock_type lock(it->second.mtx); pmem::obj::transaction::run(pmpool, [&] { it->second.val.assign(value.data(), value.size()); }); } return status::OK; } status csmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); unique_global_lock_type lock(mtx); return container->unsafe_erase(key) > 0 ? status::OK : status::NOT_FOUND; } void csmap::Recover() { if (!OID_IS_NULL(*root_oid)) { auto pmem_ptr = static_cast( pmemobj_direct(*root_oid)); container = &pmem_ptr->map; container->runtime_initialize(); container->key_comp().runtime_initialize( internal::extract_comparator(*config)); } else { pmem::obj::transaction::run(pmpool, [&] { pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent() .raw(); auto pmem_ptr = static_cast( pmemobj_direct(*root_oid)); container = &pmem_ptr->map; container->runtime_initialize(); container->key_comp().initialize( internal::extract_comparator(*config)); }); } } internal::iterator_base *csmap::new_iterator() { return new csmap_iterator{container, mtx}; } internal::iterator_base *csmap::new_const_iterator() { return new csmap_iterator{container, mtx}; } csmap::csmap_iterator::csmap_iterator(container_type *c, global_mutex_type &mtx) : container(c), lock(mtx), pop(pmem::obj::pool_by_vptr(c)) { } csmap::csmap_iterator::csmap_iterator(container_type *c, global_mutex_type &mtx) : csmap::csmap_iterator(c, mtx) { } status csmap::csmap_iterator::seek(string_view key) { init_seek(); it_ = container->find(key); if (it_ == container->end()) { return status::NOT_FOUND; } node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::seek_lower(string_view key) { init_seek(); it_ = container->find_lower(key); if (it_ == container->end()) return status::NOT_FOUND; node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::seek_lower_eq(string_view key) { init_seek(); it_ = container->find_lower_eq(key); if (it_ == container->end()) return status::NOT_FOUND; node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::seek_higher(string_view key) { init_seek(); it_ = container->find_higher(key); if (it_ == container->end()) return status::NOT_FOUND; node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::seek_higher_eq(string_view key) { init_seek(); it_ = container->find_higher_eq(key); if (it_ == container->end()) return status::NOT_FOUND; node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::seek_to_first() { init_seek(); if (container->empty()) return status::NOT_FOUND; it_ = container->begin(); node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } status csmap::csmap_iterator::is_next() { auto tmp = it_; if (tmp == container->end() || ++tmp == container->end()) return status::NOT_FOUND; return status::OK; } status csmap::csmap_iterator::next() { init_seek(); if (it_ == container->end() || ++it_ == container->end()) return status::NOT_FOUND; node_lock = csmap::unique_node_lock_type(it_->second.mtx); return status::OK; } result csmap::csmap_iterator::key() { assert(it_ != container->end()); return string_view(it_->first.data(), it_->first.length()); } result> csmap::csmap_iterator::read_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.val.size() || pos + n < pos) n = it_->second.val.size() - pos; return {it_->second.val.crange(pos, n)}; } result> csmap::csmap_iterator::write_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.val.size() || pos + n < pos) n = it_->second.val.size() - pos; log.push_back({{it_->second.val.cdata() + pos, n}, pos}); auto &val = log.back().first; return {{&val[0], &val[n]}}; } status csmap::csmap_iterator::commit() { pmem::obj::transaction::run(pop, [&] { for (auto &p : log) { auto dest = it_->second.val.range(p.second, p.first.size()); std::copy(p.first.begin(), p.first.end(), dest.begin()); } }); log.clear(); return status::OK; } void csmap::csmap_iterator::abort() { log.clear(); } void csmap::csmap_iterator::init_seek() { if (it_ != container->end()) node_lock.unlock(); } void csmap::csmap_iterator::init_seek() { csmap::csmap_iterator::init_seek(); log.clear(); } static factory_registerer register_csmap(std::unique_ptr(new csmap_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-experimental/csmap.h000066400000000000000000000124041410000423300211660ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #ifndef LIBPMEMKV_CSMAP_H #define LIBPMEMKV_CSMAP_H #include "../comparator/pmemobj_comparator.h" #include "../pmemobj_engine.h" #include #include #include #include #include #include namespace pmem { namespace kv { namespace internal { namespace csmap { using key_type = pmem::obj::string; static_assert(sizeof(key_type) == 32, ""); struct mapped_type { mapped_type() = default; mapped_type(const mapped_type &other) : val(other.val) { } mapped_type(mapped_type &&other) : val(std::move(other.val)) { } mapped_type(const std::string &str) : val(str) { } mapped_type(string_view str) : val(str.data(), str.size()) { } pmem::obj::shared_mutex mtx; pmem::obj::string val; }; static_assert(sizeof(mapped_type) == 96, ""); using map_type = pmem::obj::experimental::concurrent_map; struct pmem_type { pmem_type() : map() { std::memset(reserved, 0, sizeof(reserved)); } map_type map; uint64_t reserved[8]; }; static_assert(sizeof(pmem_type) == sizeof(map_type) + 64, ""); } /* namespace csmap */ } /* namespace internal */ class csmap : public pmemobj_engine_base { template class csmap_iterator; public: csmap(std::unique_ptr cfg); ~csmap(); csmap(const csmap &) = delete; csmap &operator=(const csmap &) = delete; 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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; private: using node_mutex_type = pmem::obj::shared_mutex; using global_mutex_type = std::shared_timed_mutex; using shared_global_lock_type = std::shared_lock; using unique_global_lock_type = std::unique_lock; using shared_node_lock_type = std::shared_lock; using unique_node_lock_type = std::unique_lock; using container_type = internal::csmap::map_type; void Recover(); status iterate(typename container_type::iterator first, typename container_type::iterator last, get_kv_callback *callback, void *arg); /* * We take read lock for thread-safe methods (like get/insert/get_all) to * synchronize with unsafe_erase() which is not thread-safe. */ global_mutex_type mtx; container_type *container; std::unique_ptr config; }; template <> class csmap::csmap_iterator : public internal::iterator_base { using container_type = csmap::container_type; public: csmap_iterator(container_type *container, global_mutex_type &mtx); status seek(string_view key) final; status seek_lower(string_view key) final; status seek_lower_eq(string_view key) final; status seek_higher(string_view key) final; status seek_higher_eq(string_view key) final; status seek_to_first() final; status is_next() final; status next() final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; container_type::iterator it_; csmap::shared_global_lock_type lock; csmap::unique_node_lock_type node_lock; pmem::obj::pool_base pop; void init_seek(); }; template <> class csmap::csmap_iterator : public csmap::csmap_iterator { using container_type = csmap::container_type; public: csmap_iterator(container_type *container, global_mutex_type &mtx); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; void init_seek() final; }; class csmap_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); return std::unique_ptr(new csmap(std::move(cfg))); }; std::string get_name() override { return "csmap"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_CSMAP_H */ pmemkv-1.5.0/src/engines-experimental/radix.cc000066400000000000000000001001621410000423300213270ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "radix.h" #include "../out.h" namespace pmem { namespace kv { namespace internal { namespace radix { transaction::transaction(pmem::obj::pool_base &pop, map_type *container) : pop(pop), container(container) { } status transaction::put(string_view key, string_view value) { log.insert(key, value); return status::OK; } status transaction::remove(string_view key) { log.remove(key); return status::OK; } status transaction::commit() { auto insert_cb = [&](const dram_log::element_type &e) { auto result = container->try_emplace(e.first, e.second); if (result.second == false) result.first.assign_val(e.second); }; auto remove_cb = [&](const dram_log::element_type &e) { container->erase(e.first); }; pmem::obj::transaction::run(pop, [&] { log.foreach (insert_cb, remove_cb); }); log.clear(); return status::OK; } void transaction::abort() { log.clear(); } } /* namespace radix */ } /* namespace internal */ radix::radix(std::unique_ptr cfg) : pmemobj_engine_base(cfg, "pmemkv_radix"), config(std::move(cfg)) { Recover(); LOG("Started ok"); } radix::~radix() { LOG("Stopped ok"); } std::string radix::name() { return "radix"; } template static status iterate_generic(Iterator first, Callback &&cb, Predicate &&p) { for (auto it = first; p(it); ++it) { auto ret = cb(it); if (ret != 0) return status::STOPPED_BY_CB; } return status::OK; } status radix::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); cnt = container->size(); return status::OK; } status radix::count_above(string_view key, std::size_t &cnt) { LOG("count_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->upper_bound(key); auto last = container->end(); cnt = internal::distance(first, last); return status::OK; } status radix::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_equal_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->lower_bound(key); auto last = container->end(); cnt = internal::distance(first, last); return status::OK; } status radix::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_equal_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->begin(); auto last = container->upper_bound(key); cnt = internal::distance(first, last); return status::OK; } status radix::count_below(string_view key, std::size_t &cnt) { LOG("count_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->begin(); auto last = container->lower_bound(key); cnt = internal::distance(first, last); return status::OK; } status radix::count_between(string_view key1, string_view key2, std::size_t &cnt) { LOG("count_between for key1=" << key1.data() << ", key2=" << key2.data()); check_outside_tx(); if (key1.compare(key2) < 0) { auto first = container->upper_bound(key1); auto last = container->lower_bound(key2); cnt = internal::distance(first, last); } else { cnt = 0; } return status::OK; } int radix::iterate_callback(const container_type::iterator &it, get_kv_callback *callback, void *arg) { const auto &key = it->key(); const auto &value = it->value(); return callback(key.data(), key.size(), value.data(), value.size(), arg); } status radix::iterate(container_type::iterator first, container_type::iterator last, get_kv_callback *callback, void *arg) { return iterate_generic( first, [&](const container_type::iterator &it) { return iterate_callback(it, callback, arg); }, [&](const container_type::iterator &it) { return it != last; }); } status radix::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); auto first = container->begin(); auto last = container->end(); return iterate(first, last, callback, arg); } status radix::get_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->upper_bound(key); auto last = container->end(); return iterate(first, last, callback, arg); } status radix::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_above for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->lower_bound(key); auto last = container->end(); return iterate(first, last, callback, arg); } status radix::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->begin(); auto last = container->upper_bound(key); return iterate(first, last, callback, arg); } status radix::get_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_below for key=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = container->begin(); auto last = container->lower_bound(key); return iterate(first, last, callback, arg); } status radix::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { LOG("get_between for key1=" << key1.data() << ", key2=" << key2.data()); check_outside_tx(); if (key1.compare(key2) < 0) { auto first = container->upper_bound(key1); auto last = container->lower_bound(key2); return iterate(first, last, callback, arg); } return status::OK; } status radix::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); return container->find(key) != container->end() ? status::OK : status::NOT_FOUND; } status radix::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); check_outside_tx(); auto it = container->find(key); if (it != container->end()) { auto value = string_view(it->value()); callback(value.data(), value.size(), arg); return status::OK; } LOG(" key not found"); return status::NOT_FOUND; } status radix::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 = container->try_emplace(key, value); if (result.second == false) { pmem::obj::transaction::run(pmpool, [&] { result.first.assign_val(value); }); } return status::OK; } status radix::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); auto it = container->find(key); if (it == container->end()) return status::NOT_FOUND; container->erase(it); return status::OK; } internal::transaction *radix::begin_tx() { return new internal::radix::transaction(pmpool, container); } void radix::Recover() { if (!OID_IS_NULL(*root_oid)) { auto pmem_ptr = static_cast(pmemobj_direct(*root_oid)); container = &pmem_ptr->map; } else { pmem::obj::transaction::run(pmpool, [&] { pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent().raw(); auto pmem_ptr = static_cast(pmemobj_direct(*root_oid)); container = &pmem_ptr->map; }); } } /* HETEROGENEOUS_RADIX */ static void no_delete(const char *) { /* do nothing */ } /* Must be called inside EBR critical section. */ heterogeneous_radix::merged_iterator::merged_iterator( heterogeneous_radix &hetero_radix, typename internal::radix::ordered_cache::iterator dram_it, typename internal::radix::map_mt_type::iterator pmem_it) : hetero_radix(hetero_radix), dram_it(dram_it), pmem_it(pmem_it) { set_current_it(); } /* Must be called inside EBR critical section. */ heterogeneous_radix::merged_iterator &heterogeneous_radix::merged_iterator::operator++() { assert(dereferenceable()); if (curr_it == current_it::dram) { assert(dram_it != hetero_radix.cache->end()); assert(pmem_it == hetero_radix.container->end() || dram_it->first.compare(pmem_it->key()) < 0); } else { assert(pmem_it != hetero_radix.container->end()); assert(dram_it == hetero_radix.cache->end() || dram_it->first.compare(pmem_it->key()) > 0); } if (curr_it == current_it::dram) ++dram_it; else ++pmem_it; set_current_it(); return *this; } /* Must be called inside EBR critical section. */ string_view heterogeneous_radix::merged_iterator::key() const { assert(dereferenceable()); if (curr_it == current_it::dram) return dram_it->first; else return pmem_it->key(); } /* Must be called inside EBR critical section. */ std::pair heterogeneous_radix::merged_iterator::value() const { assert(dereferenceable()); if (curr_it == current_it::dram) { auto v = &dram_it->second->second; auto value = v->load(std::memory_order_acquire); assert(value != tombstone_persistent() && value != tombstone_volatile()); auto size = value->size(); unique_ptr_type ptr(nullptr, &no_delete); while (!ptr) ptr = hetero_radix.try_read_value(v, value); return std::pair(std::move(ptr), size); } else { const auto &val = pmem_it->value(); unique_ptr_type ptr(val.data(), no_delete); return std::pair(std::move(ptr), val.size()); } } bool heterogeneous_radix::merged_iterator::dereferenceable() const { return pmem_it != hetero_radix.container->end() || dram_it != hetero_radix.cache->end(); } void heterogeneous_radix::merged_iterator::set_current_it() { while (dereferenceable()) { if (pmem_it != hetero_radix.container->end() && dram_it != hetero_radix.cache->end() && dram_it->first == string_view(pmem_it->key())) { /* If keys are the same, skip the one in pmem (the dram one is * more recent) */ ++pmem_it; } else if (dram_it == hetero_radix.cache->end() || (pmem_it != hetero_radix.container->end() && dram_it->first.compare(pmem_it->key()) > 0)) { /* If there are no more dram elements or pmem element is smaller */ curr_it = current_it::pmem; return; } else { auto v = dram_it->second->second.load(std::memory_order_acquire); /* Skip removed entries */ if (v == heterogeneous_radix::tombstone_volatile() || v == heterogeneous_radix::tombstone_persistent()) { ++dram_it; } else { curr_it = current_it::dram; return; } } } } const heterogeneous_radix::uvalue_type *heterogeneous_radix::tombstone_volatile() { return reinterpret_cast(1ULL); } const heterogeneous_radix::uvalue_type *heterogeneous_radix::tombstone_persistent() { return reinterpret_cast(2ULL); } heterogeneous_radix::heterogeneous_radix(std::unique_ptr cfg) : pmemobj_engine_base(cfg, "pmemkv_radix"), config(std::move(cfg)) { config->get_uint64("log_size", &log_size); config->get_uint64("cache_size", &cache_size); pmem_type *pmem_ptr; if (!OID_IS_NULL(*root_oid)) { pmem_ptr = static_cast(pmemobj_direct(*root_oid)); } else { pmem::obj::transaction::run(pmpool, [&] { pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent().raw(); pmem_ptr = static_cast(pmemobj_direct(*root_oid)); pmem_ptr->log = pmem::obj::make_persistent( log_size); }); } log = pmem_ptr->log.get(); container = &pmem_ptr->map; container->runtime_initialize_mt(); container_worker = std::unique_ptr( new container_type::ebr::worker(container->register_worker())); cache = std::unique_ptr(new cache_type(cache_size)); queue = std::unique_ptr(new pmem_queue_type(*pmem_ptr->log, 1)); queue_worker = std::unique_ptr( new pmem_queue_type::worker(queue->register_worker())); queue->try_consume_batch([&](pmem_queue_type::batch_type batch) { for (auto entry : batch) consume_queue_entry(entry, false); }); bg_exception_ptr = nullptr; stopped.store(false); bg_thread = std::thread([&] { bg_work(); }); pop = pmem::obj::pool_by_vptr(&pmem_ptr->log); } heterogeneous_radix::~heterogeneous_radix() { { std::unique_lock lock(bg_lock); stopped.store(true); } bg_cv.notify_one(); bg_thread.join(); container_worker.reset(nullptr); queue_worker.reset(nullptr); container->runtime_finalize_mt(); } bool heterogeneous_radix::log_contains(const void *ptr) const { auto begin = log->data().data(); auto size = log->data().size(); return (begin <= reinterpret_cast(ptr)) && (begin + size >= reinterpret_cast(ptr)); } heterogeneous_radix::cache_type::value_type * heterogeneous_radix::cache_put_with_evict(string_view key, const uvalue_type *value) { auto evict_cb = [&](typename cache_type::lru_list_type &list) { for (auto rit = list.rbegin(); rit != list.rend(); rit++) { auto t = rit->second.load(std::memory_order_relaxed); /* Only element which has already been process by background * thread (is not in the log) can be evicted. */ if (!log_contains(t) && t != tombstone_volatile()) return std::next(rit).base(); } return list.end(); }; return cache->put(key, value, evict_cb); } void heterogeneous_radix::handle_oom_from_bg() { std::exception_ptr *exc; if ((exc = bg_exception_ptr.load(std::memory_order_acquire)) != nullptr) { std::rethrow_exception(*exc); } } status heterogeneous_radix::put(string_view key, string_view value) { check_outside_tx(); /* * This implementation consists of following steps: * 1. Insert element to the DRAM cache (with empty value for now) * 2. Allocate queue_entry on dram (it will hold key/value or * key/tombstone pair). * 3. Try to produce the queue_entry using queue. If this succeeds * set cache entry value to point to the value in queue. * * If inserting to cache or producing the queue_entry fails, check * if background thread did not encounter oom. If yes, propagate * oom to the user. */ auto uvalue_key_size = pmem::obj::experimental::total_sizeof::value(key); auto padding = align_up(uvalue_key_size, alignof(uvalue_type)) - uvalue_key_size; auto req_size = uvalue_key_size + pmem::obj::experimental::total_sizeof::value(value) + sizeof(queue_entry) + padding; using alloc_type = typename std::aligned_storage< sizeof(queue_entry), alignof(queue_entry)>::type; auto alloc_size = (req_size + sizeof(queue_entry) - 1) / sizeof(queue_entry); auto data = std::unique_ptr(new alloc_type[alloc_size]); assert(reinterpret_cast(data.get()) % alignof(queue_entry) == 0); /* XXX: implement blocking cache_put_with_evict */ cache_type::value_type *cache_val = nullptr; while (cache_val == nullptr) { cache_val = cache_put_with_evict(key, nullptr); handle_oom_from_bg(); } new (data.get()) queue_entry(cache_val, key, value); while (true) { auto produced = queue_worker->try_produce( pmem::obj::string_view(reinterpret_cast(data.get()), req_size), [&](pmem::obj::string_view target) { auto pmem_entry = reinterpret_cast< const queue_entry *>(target.data()); assert(pmem_entry->key() == key); const uvalue_type *val = (!value.data() ? tombstone_volatile() : &pmem_entry->value()); cache_val->store(val, std::memory_order_release); }); if (produced) break; else handle_oom_from_bg(); } // XXX - if try_produce == false, we can just allocate new radix node to // TLS and the publish pointer to this node // NEED TX support for produce(): // tx {queue.produce([&] (data) { data = make_persistent(); }) } return status::OK; } status heterogeneous_radix::remove(string_view k) { check_outside_tx(); /* Check if element exists. */ bool found = false; auto v = cache->get(k, false); if (v) { auto value = v->load(std::memory_order_acquire); found = (value != tombstone_persistent() && value != tombstone_volatile()); } else { found = container->find(k) != container->end(); } if (!found) return status::NOT_FOUND; try { /* Put tombstone. */ auto s = put(k, pmem::obj::string_view()); if (s != status::OK) return s; } catch (pmem::transaction_out_of_memory &) { /* Set element in cache to tombstone, does nothing if element * is not in the cache. */ cache->put(k, tombstone_persistent(), [&](typename cache_type::lru_list_type &list) { return list.end(); }); /* Try to free the element directly, bypassing the queue. No * synchronization is needed with bg thread since it is blocked by OOM. */ assert(bg_exception_ptr.load(std::memory_order_relaxed)); container->erase(k); container->garbage_collect_force(); delete bg_exception_ptr.load(std::memory_order_relaxed); { std::unique_lock lock(bg_lock); bg_exception_ptr.store(nullptr, std::memory_order_release); } /* Notify bg thread that the exception was consumed */ bg_cv.notify_one(); } return status::OK; } /* Tries to optimistically read from the log. Operation succeeds only * if v->load() points equals value after read completes. Otherwise nullptr * is returned and value is set to v->load() */ heterogeneous_radix::unique_ptr_type heterogeneous_radix::try_read_value(cache_type::value_type *v, const uvalue_type *&value) const { auto size = value->size(); auto data = value->data(); if (log_contains(value) && log_contains(data + size)) { return log_read_optimistically(v, value); } else { /* Cache entry points to data in pmem container. It's safe * to just read from it as entries in containers are * protected by EBR. */ auto ptr = unique_ptr_type(value->data(), &no_delete); assert(v->load(std::memory_order_acquire) == value); return ptr; } } heterogeneous_radix::unique_ptr_type heterogeneous_radix::log_read_optimistically(cache_type::value_type *v, const uvalue_type *&value) const { auto data = value->data(); auto size = value->size(); /* Cache entry points to data in log. To read the data we * must protect against producers which could overwrite * the data concurrently. To do this we use variant of * optimistic concurrency control: we validate that data * is entirely inside the log and copy the data to * temporary buffer. After that, we check if cache entry * changed. If it did, it means that the entry was * conusmed by bg thread and producers might have * overwritten the data - in this case we start from the * beginning. Otherwise we just call user callback with * the temporary buffer. */ auto unsafe_buff = new char[size]; std::copy(data, data + size, unsafe_buff); auto buffer = unique_ptr_type(unsafe_buff, [](const char *p) { delete[] p; }); auto current_value = v->load(std::memory_order_acquire); if (current_value == value) { return buffer; } else { value = current_value; return unique_ptr_type(nullptr, &no_delete); } } status heterogeneous_radix::get(string_view key, get_v_callback *callback, void *arg) { check_outside_tx(); status s = status::OK; auto v = cache->get(key, true); container_worker->critical([&] { if (!v) { /* If element is not in the cache, search radix tree. */ auto it = container->find(key); if (it != container->end()) { auto value = string_view(it->value()); callback(value.data(), value.size(), arg); cache_put_with_evict(key, &it->value()); s = status::OK; } else s = status::NOT_FOUND; return; } unique_ptr_type ptr = unique_ptr_type(nullptr, &no_delete); size_t size; while (!ptr) { auto value = v->load(std::memory_order_acquire); if (value == tombstone_volatile() || value == tombstone_persistent()) { s = status::NOT_FOUND; return; } size = value->size(); ptr = try_read_value(v, value); } callback(ptr.get(), size, arg); s = status::OK; }); return s; } std::string heterogeneous_radix::name() { return "radix"; } heterogeneous_radix::merged_iterator heterogeneous_radix::merged_begin() { return merged_iterator(*this, cache->begin(), container->begin()); } heterogeneous_radix::merged_iterator heterogeneous_radix::merged_end() { return merged_iterator(*this, cache->end(), container->end()); } heterogeneous_radix::merged_iterator heterogeneous_radix::merged_lower_bound(string_view key) { auto dram_lo = cache->lower_bound(key); auto pmem_lo = container->lower_bound(key); assert(dram_lo == cache->end() || dram_lo->first.compare(key) >= 0); assert(pmem_lo == container->end() || pmem_lo->key().compare(key) >= 0); return merged_iterator(*this, dram_lo, pmem_lo); } heterogeneous_radix::merged_iterator heterogeneous_radix::merged_upper_bound(string_view key) { auto dram_up = cache->upper_bound(key); auto pmem_up = container->upper_bound(key); assert(dram_up == cache->end() || dram_up->first.compare(key) > 0); assert(pmem_up == container->end() || pmem_up->key().compare(key) > 0); return merged_iterator(*this, dram_up, pmem_up); } int heterogeneous_radix::iterate_callback(const merged_iterator &it, get_kv_callback *callback, void *arg) { const auto &key = it.key(); auto val = it.value(); return callback(key.data(), key.size(), val.first.get(), val.second, arg); } status heterogeneous_radix::get_all(get_kv_callback *callback, void *arg) { check_outside_tx(); status s; container_worker->critical([&] { auto first = merged_begin(); s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable(); }); }); return s; } status heterogeneous_radix::get_above(string_view key, get_kv_callback *callback, void *arg) { check_outside_tx(); status s; container_worker->critical([&] { auto first = merged_upper_bound(key); s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable(); }); }); return s; } status heterogeneous_radix::get_equal_above(string_view key, get_kv_callback *callback, void *arg) { check_outside_tx(); status s; container_worker->critical([&] { auto first = merged_lower_bound(key); s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable(); }); }); return s; } status heterogeneous_radix::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { check_outside_tx(); status s; container_worker->critical([&] { auto first = merged_begin(); /* We cannot rely on iterator comparisons because of concurrent * inserts/erases. There are two problems: * 1. Iterator can be erased. In that case `while(it != last) it++;` would * spin forever. * 2. `auto last = merged_upper_bound(key); * while (it != last) it++;` * * if merged_upper_bound(key) returns end(), this means there are no * elements bigger than key - we can then return all elements * from the container, right? * * Not really - while we iterate someone might insert element bigger * than key and we will iterate over such element (since we are processing * all elements). */ s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable() && it.key().compare(key) <= 0; }); }); return s; } status heterogeneous_radix::get_below(string_view key, get_kv_callback *callback, void *arg) { check_outside_tx(); status s; container_worker->critical([&] { auto first = merged_begin(); s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable() && it.key().compare(key) < 0; }); }); return s; } status heterogeneous_radix::get_between(string_view key1, string_view key2, get_kv_callback *callback, void *arg) { check_outside_tx(); if (key1.compare(key2) < 0) { status s; container_worker->critical([&] { auto first = merged_upper_bound(key1); s = iterate_generic( first, [&](const merged_iterator &it) { return iterate_callback(it, callback, arg); }, [&](const merged_iterator &it) { return it.dereferenceable() && it.key().compare(key2) < 0; }); }); return s; } return status::OK; } /* Used as a callback for get_* methods, increments a counter passed through @param arg on * each call. */ static int count_elements(const char *, size_t, const char *, size_t, void *arg) { auto *cnt = static_cast(arg); ++(*cnt); return 0; } status heterogeneous_radix::count_all(std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_all(count_elements, (void *)&cnt); } status heterogeneous_radix::count_above(string_view key, std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_above(key, count_elements, (void *)&cnt); } status heterogeneous_radix::count_equal_above(string_view key, std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_equal_above(key, count_elements, (void *)&cnt); } status heterogeneous_radix::count_equal_below(string_view key, std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_equal_below(key, count_elements, (void *)&cnt); } status heterogeneous_radix::count_below(string_view key, std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_below(key, count_elements, (void *)&cnt); } status heterogeneous_radix::count_between(string_view key1, string_view key2, std::size_t &cnt) { check_outside_tx(); cnt = 0; return get_between(key1, key2, count_elements, (void *)&cnt); } status heterogeneous_radix::exists(string_view key) { return get( key, [](const char *, size_t, void *) {}, nullptr); } void heterogeneous_radix::consume_queue_entry(pmem::obj::string_view entry, bool dram_is_valid) { /* * This function consumes entries for the queue. It inserts/erases the * element from the radix tree. * * If dram_is_valid is set it also exchanges pointer in dram_entry so * that it now points to radix_tree node (or tombstone for erase). * This can only be done if dram_entry was allocated in this application * run (so, only from bg_work()). * * This allows us to keep processed elements in cache without additional * value copies. */ auto e = reinterpret_cast *>(entry.data()); auto dram_entry = const_cast *>(e)->dram_entry; const uvalue_type *expected = e->remove ? tombstone_volatile() : &e->value(); const uvalue_type *desired; /* If the dram_entry points to different element than was passed through queue * it is already outdated - just skip it, it will be handled later. */ if (dram_is_valid && dram_entry->load(std::memory_order_acquire) != expected) return; if (e->remove) { container->erase(e->key()); desired = tombstone_persistent(); } else { auto ret = container->insert_or_assign(e->key(), e->value()); desired = &ret.first->value(); } if (dram_is_valid) dram_entry->compare_exchange_strong(expected, desired); } void heterogeneous_radix::bg_work() { bool should_report_oom = false; while (true) { /* XXX: only stop if all elements are consumed ? */ if (stopped.load()) return; try { auto consumed = queue->try_consume_batch( [&](pmem_queue_type::batch_type batch) { for (auto entry : batch) consume_queue_entry(entry, true); }); if (consumed) { should_report_oom = false; } else { /* Nothing else to do, try to collect some * garbage. */ container->garbage_collect(); } } catch (...) { if (!should_report_oom) { /* Try to force clear garbage. If that fails, we will * report oom for the user in next iteration. */ try { should_report_oom = true; container->garbage_collect_force(); continue; } catch (...) { } } auto ex = new std::exception_ptr(std::current_exception()); bg_exception_ptr.store(ex); std::unique_lock lock(bg_lock); /* Wait until exception is handled or * engine is stopped. */ bg_cv.wait(lock, [&] { return bg_exception_ptr.load() == nullptr || stopped.load(); }); /* If engine is stopped, abort consume */ if (stopped.load()) return; should_report_oom = false; } } } internal::iterator_base *radix::new_iterator() { return new radix_iterator{container}; } internal::iterator_base *radix::new_const_iterator() { return new radix_iterator{container}; } radix::radix_iterator::radix_iterator(container_type *c) : container(c), pop(pmem::obj::pool_by_vptr(c)) { } radix::radix_iterator::radix_iterator(container_type *c) : radix::radix_iterator(c) { } status radix::radix_iterator::seek(string_view key) { init_seek(); it_ = container->find(key); if (it_ != container->end()) return status::OK; return status::NOT_FOUND; } status radix::radix_iterator::seek_lower(string_view key) { init_seek(); it_ = container->lower_bound(key); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status radix::radix_iterator::seek_lower_eq(string_view key) { init_seek(); it_ = container->upper_bound(key); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status radix::radix_iterator::seek_higher(string_view key) { init_seek(); it_ = container->upper_bound(key); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status radix::radix_iterator::seek_higher_eq(string_view key) { init_seek(); it_ = container->lower_bound(key); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status radix::radix_iterator::seek_to_first() { init_seek(); if (container->empty()) return status::NOT_FOUND; it_ = container->begin(); return status::OK; } status radix::radix_iterator::seek_to_last() { init_seek(); if (container->empty()) return status::NOT_FOUND; it_ = container->end(); --it_; return status::OK; } status radix::radix_iterator::is_next() { auto tmp = it_; if (tmp == container->end() || ++tmp == container->end()) return status::NOT_FOUND; return status::OK; } status radix::radix_iterator::next() { init_seek(); if (it_ == container->end() || ++it_ == container->end()) return status::NOT_FOUND; return status::OK; } status radix::radix_iterator::prev() { init_seek(); if (it_ == container->begin()) return status::NOT_FOUND; --it_; return status::OK; } result radix::radix_iterator::key() { assert(it_ != container->end()); return string_view(it_->key().cdata(), it_->key().size()); } result> radix::radix_iterator::read_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->value().size() || pos + n < pos) n = it_->value().size() - pos; return {{it_->value().cdata() + pos, it_->value().cdata() + pos + n}}; } result> radix::radix_iterator::write_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->value().size() || pos + n < pos) n = it_->value().size() - pos; log.push_back({std::string(it_->value().cdata() + pos, n), pos}); auto &val = log.back().first; return {{&val[0], &val[n]}}; } status radix::radix_iterator::commit() { pmem::obj::transaction::run(pop, [&] { for (auto &p : log) { auto dest = it_->value().range(p.second, p.first.size()); std::copy(p.first.begin(), p.first.end(), dest.begin()); } }); log.clear(); return status::OK; } void radix::radix_iterator::abort() { log.clear(); } static factory_registerer register_radix(std::unique_ptr(new radix_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-experimental/radix.h000066400000000000000000000370071410000423300212000ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #ifndef LIBPMEMKV_RADIX_H #define LIBPMEMKV_RADIX_H #include "../comparator/pmemobj_comparator.h" #include "../iterator.h" #include "../pmemobj_engine.h" #include #include #include #include #include #include #include #include #include namespace pmem { namespace kv { namespace internal { namespace radix { using key_value_type = pmem::obj::experimental::inline_string; template using map_type_generic = pmem::obj::experimental::radix_tree< key_value_type, key_value_type, pmem::obj::experimental::bytes_view, Mt>; using map_type = map_type_generic; using map_mt_type = map_type_generic; using log_type = pmem::obj::experimental::mpsc_queue::pmem_log_type; template struct pmem_type { pmem_type() : map() { std::memset(reserved, 0, sizeof(reserved)); } MapType map; pmem::obj::persistent_ptr log; uint64_t reserved[6]; }; static_assert(sizeof(pmem_type) == sizeof(map_type) + 64, ""); class transaction : public ::pmem::kv::internal::transaction { public: transaction(pmem::obj::pool_base &pop, map_type *container); status put(string_view key, string_view value) final; status remove(string_view key) final; status commit() final; void abort() final; private: pmem::obj::pool_base &pop; dram_log log; map_type *container; }; template class ordered_cache { private: using key_type = std::string; using dram_value_type = std::pair>; using dram_map_type = std::map::iterator>; public: using lru_list_type = std::list; using iterator = typename dram_map_type::iterator; using value_type = std::atomic; ordered_cache(size_t max_size) : max_size(max_size) { } ordered_cache(const ordered_cache &) = delete; ordered_cache(ordered_cache &&) = delete; ordered_cache &operator=(const ordered_cache &) = delete; ordered_cache &operator=(ordered_cache &&) = delete; template value_type *put(string_view key, const Value *v, F &&evict) { assert(map.size() == lru_list.size()); auto it = map.find(key); if (it == map.end()) { if (lru_list.size() < max_size) { lru_list.emplace_front( std::piecewise_construct, std::forward_as_tuple(key.data(), key.size()), std::forward_as_tuple(v)); auto lit = lru_list.begin(); auto ret = map_put(lit->first, lit); assert(ret.second); it = ret.first; } else { auto lit = evict(lru_list); if (lit == lru_list.end()) return nullptr; map.erase(lit->first); lru_list.splice(lru_list.begin(), lru_list, lit); lru_list.begin()->first.assign(key.data(), key.size()); lru_list.begin()->second.store(v, std::memory_order_release); lit = lru_list.begin(); auto ret = map_put(lit->first, lit); assert(ret.second); it = ret.first; } } else { lru_list.splice(lru_list.begin(), lru_list, it->second); lru_list.begin()->second.store(v, std::memory_order_release); } return &it->second->second; } value_type *get(string_view key, bool promote) { assert(map.size() == lru_list.size()); auto it = map.find(key); if (it == map.end()) { return nullptr; } else { if (promote) lru_list.splice(lru_list.begin(), lru_list, it->second); return &it->second->second; } } iterator begin() { return map.begin(); } iterator end() { return map.end(); } iterator lower_bound(string_view key) { return map.lower_bound(key); } iterator upper_bound(string_view key) { return map.upper_bound(key); } private: std::pair map_put(const typename dram_map_type::key_type &k, const typename dram_map_type::mapped_type &v) { #if __cpp_lib_map_try_emplace return map.try_emplace(k, v); #else return map.emplace(k, v); #endif } lru_list_type lru_list; dram_map_type map; const size_t max_size; }; } /* namespace radix */ } /* namespace internal */ /** * Radix tree engine backed by: * https://github.com/pmem/libpmemobj-cpp/blob/master/include/libpmemobj%2B%2B/experimental/radix_tree.hpp * * It is a sorted, singlethreaded engine. Unlike other sorted engines it does not support * custom comparator (the order is defined by the keys' representation). * * The implementation is a variation of a PATRICIA trie - the internal * nodes do not store the path explicitly, but only a position at which * the keys differ. Keys are stored entirely in leafs. * * More info about radix tree: https://en.wikipedia.org/wiki/Radix_tree */ class radix : public pmemobj_engine_base> { template class radix_iterator; public: radix(std::unique_ptr cfg); ~radix(); radix(const radix &) = delete; radix &operator=(const radix &) = delete; 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; internal::transaction *begin_tx() final; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; private: using container_type = internal::radix::map_type; using pmem_type = internal::radix::pmem_type; void Recover(); int iterate_callback(const container_type::iterator &it, get_kv_callback *callback, void *arg); status iterate(container_type::iterator begin, container_type::iterator last, get_kv_callback *callback, void *arg); container_type *container; std::unique_ptr config; }; /** * Heterogenous engine which implements DRAM cache on top of radix tree container. * * On put, data is first inserted to DRAM cache and appended to pmem log (mpsc_queue). * There is one background thread which consumes data from the log and erases/inserts * consumed elements to the radix tree. * * On get, dram cache is first checked. If looked-for element is found there, it is * returned to the user. On cache-miss, we search the radix_tree. Read operations on * radix_tree are protected by Epoch Based Reclamation mechanism. */ class heterogeneous_radix : public pmemobj_engine_base< internal::radix::pmem_type> { public: heterogeneous_radix(std::unique_ptr cfg); ~heterogeneous_radix(); heterogeneous_radix(const heterogeneous_radix &) = delete; heterogeneous_radix &operator=(const heterogeneous_radix &) = delete; 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 put(string_view key, string_view value) final; status remove(string_view k) final; status get(string_view key, get_v_callback *callback, void *arg) final; private: using container_type = internal::radix::map_mt_type; using pmem_type = internal::radix::pmem_type; using uvalue_type = pmem::obj::experimental::inline_string; using dram_uvalue_type = pmem::obj::experimental::dram_inline_string; using cache_type = internal::radix::ordered_cache; using pmem_log_type = internal::radix::log_type; using unique_ptr_type = std::unique_ptr; using dram_iterator = typename cache_type::iterator; using pmem_iterator = typename container_type::iterator; template struct queue_entry { queue_entry(cache_type::value_type *dram_entry, string_view key_, string_view value_); const KeyValueType &key() const; const KeyValueType &value() const; KeyValueType &value(); cache_type::value_type *dram_entry; bool remove; char padding[7]; }; static_assert(sizeof(queue_entry) == 16, "queue_entry too big"); using pmem_queue_type = pmem::obj::experimental::mpsc_queue; struct merged_iterator { merged_iterator(heterogeneous_radix &hetero_radix, dram_iterator dram_it, pmem_iterator pmem_it); merged_iterator(const merged_iterator &) = default; merged_iterator &operator++(); bool dereferenceable() const; string_view key() const; std::pair value() const; private: heterogeneous_radix &hetero_radix; dram_iterator dram_it; pmem_iterator pmem_it; enum class current_it { dram, pmem } curr_it; void set_current_it(); }; friend struct merged_iterator; merged_iterator merged_begin(); merged_iterator merged_end(); merged_iterator merged_lower_bound(string_view key); merged_iterator merged_upper_bound(string_view key); int iterate_callback(const merged_iterator &it, get_kv_callback *callback, void *arg); void bg_work(); cache_type::value_type *cache_put_with_evict(string_view key, const uvalue_type *value); bool log_contains(const void *entry) const; void handle_oom_from_bg(); void consume_queue_entry(pmem::obj::string_view item, bool); unique_ptr_type log_read_optimistically(cache_type::value_type *ptr, const uvalue_type *&) const; unique_ptr_type try_read_value(cache_type::value_type *ptr, const uvalue_type *&value) const; /* Element was logically removed but there might be an older version on pmem. */ static const uvalue_type *tombstone_volatile(); /* Element logically removed from cache and from pmem. */ static const uvalue_type *tombstone_persistent(); std::unique_ptr cache; size_t cache_size = 64000000; size_t log_size = 1000000; std::atomic stopped; std::thread bg_thread; pmem::obj::pool_base pop; container_type *container; std::unique_ptr container_worker; pmem_log_type *log; std::unique_ptr config; std::mutex eviction_lock; std::condition_variable eviction_cv; std::mutex bg_lock; std::condition_variable bg_cv; std::atomic bg_exception_ptr; std::unique_ptr queue; std::unique_ptr queue_worker; }; static inline constexpr size_t align_up(size_t size, size_t align) { return ((size) + (align)-1) & ~((align)-1); } template heterogeneous_radix::queue_entry::queue_entry( heterogeneous_radix::cache_type::value_type *dram_entry, string_view key_, string_view value_) : dram_entry(dram_entry) { auto key_size = pmem::obj::experimental::total_sizeof::value(key_); auto key_dst = reinterpret_cast(this + 1); auto padding = align_up(key_size, alignof(KeyValueType)) - key_size; auto val_dst = reinterpret_cast( reinterpret_cast(key_dst) + key_size + padding); new (key_dst) KeyValueType(key_); if (value_.data() == nullptr) { new (val_dst) KeyValueType(string_view("")); remove = true; } else { new (val_dst) KeyValueType(value_); remove = false; } } /* Returns reference to the key which is held right after queue_entry struct. */ template const KeyValueType &heterogeneous_radix::queue_entry::key() const { auto key = reinterpret_cast(this + 1); return *key; } /* Returns reference to the value which is held after the key (and optional padding). */ template const KeyValueType &heterogeneous_radix::queue_entry::value() const { auto key_size = pmem::obj::experimental::total_sizeof::value( string_view(key())); auto key_dst = reinterpret_cast(this + 1); auto padding = align_up(key_size, alignof(KeyValueType)) - key_size; auto val_dst = reinterpret_cast(key_dst + key_size + padding); return *reinterpret_cast(val_dst); } template KeyValueType &heterogeneous_radix::queue_entry::value() { auto val = &const_cast *>(this)->value(); return *const_cast(val); } template <> class radix::radix_iterator : public internal::iterator_base { using container_type = radix::container_type; public: radix_iterator(container_type *container); status seek(string_view key) final; status seek_lower(string_view key) final; status seek_lower_eq(string_view key) final; status seek_higher(string_view key) final; status seek_higher_eq(string_view key) final; status seek_to_first() final; status seek_to_last() final; status is_next() final; status next() final; status prev() final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; container_type::iterator it_; pmem::obj::pool_base pop; }; template <> class radix::radix_iterator : public radix::radix_iterator { using container_type = radix::container_type; public: radix_iterator(container_type *container); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; }; class radix_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); uint64_t dram_caching; if (cfg->get_uint64("dram_caching", &dram_caching) && dram_caching) { return std::unique_ptr( new heterogeneous_radix(std::move(cfg))); } else { return std::unique_ptr(new radix(std::move(cfg))); } }; std::string get_name() override { return "radix"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_RADIX_H */ pmemkv-1.5.0/src/engines-experimental/robinhood.cc000066400000000000000000000440131410000423300222050ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "robinhood.h" #include "../exceptions.h" #include "../fast_hash.h" #include "../out.h" #include namespace pmem { namespace kv { namespace internal { namespace robinhood { static std::string name() { return "robinhood"; } static float get_load_factor() { auto lf = std::getenv("PMEMKV_ROBINHOOD_LOAD_FACTOR"); if (lf) return std::stof(lf); else return HASHMAP_RP_LOAD_FACTOR; } /* * entry_is_deleted -- checks 'tombstone' bit if hash is deleted */ static inline int entry_is_deleted(uint64_t hash) { return (hash & TOMBSTONE_MASK) > 0; } /* * entry_is_empty -- checks if entry is empty */ static inline int entry_is_empty(uint64_t hash) { return hash == 0 || entry_is_deleted(hash); } /* * increment_pos -- increment position index, skip 0 */ static uint64_t increment_pos(const struct hashmap_rp *hashmap, uint64_t pos) { pos = (pos + 1) & (hashmap->capacity - 1); return pos == 0 ? 1 : pos; } /* * probe_distance -- returns probe number, an indicator how far from * desired position given hash is stored in hashmap */ static uint64_t probe_distance(const struct hashmap_rp *hashmap, uint64_t hash_key, uint64_t slot_index) { uint64_t capacity = hashmap->capacity; return static_cast(slot_index + capacity - hash_key) & (capacity - 1); } /* * hash -- hash function based on Austin Appleby MurmurHash3 64-bit finalizer. * Returned value is modified to work with special values for unused and * deleted hashes. */ static uint64_t hash(const struct hashmap_rp *hashmap, uint64_t key) { key ^= key >> 33; key *= 0xff51afd7ed558ccd; key ^= key >> 33; key *= 0xc4ceb9fe1a85ec53; key ^= key >> 33; key &= hashmap->capacity - 1; /* first, 'tombstone' bit is used to indicate deleted item */ key &= ~TOMBSTONE_MASK; /* * Ensure that we never return 0 as a hash, since we use 0 to * indicate that element has never been used at all. */ return key == 0 ? 1 : key; } /* * hashmap_create -- hashmap initializer */ static void hashmap_create(PMEMobjpool *pop, TOID(struct hashmap_rp) * hashmap_p, std::vector &actv) { actv.emplace_back(); TOID(struct hashmap_rp) hashmap = POBJ_RESERVE_NEW(pop, struct hashmap_rp, &actv.back()); if (TOID_IS_NULL(hashmap)) { LOG(std::string("hashmap alloc failed: ") + pmemobj_errormsg()); pmemobj_cancel(pop, actv.data(), actv.size()); abort(); } D_RW(hashmap)->count = 0; D_RW(hashmap)->capacity = INIT_ENTRIES_NUM_RP; D_RW(hashmap)->load_factor = get_load_factor(); D_RW(hashmap)->resize_threshold = static_cast(INIT_ENTRIES_NUM_RP * D_RO(hashmap)->load_factor); size_t sz = sizeof(struct entry) * D_RO(hashmap)->capacity; /* init entries with zero in order to track unused hashes */ actv.emplace_back(); D_RW(hashmap)->entries = POBJ_XRESERVE_ALLOC(pop, struct entry, sz, &actv.back(), POBJ_XALLOC_ZERO); if (TOID_IS_NULL(D_RO(hashmap)->entries)) { LOG(std::string("hashmap alloc failed: ") + pmemobj_errormsg()); pmemobj_cancel(pop, actv.data(), actv.size()); abort(); } pmemobj_persist(pop, D_RW(hashmap), sizeof(struct hashmap_rp)); actv.emplace_back(); pmemobj_set_value(pop, &actv.back(), &(hashmap_p->oid.pool_uuid_lo), hashmap.oid.pool_uuid_lo); actv.emplace_back(); pmemobj_set_value(pop, &actv.back(), &(hashmap_p->oid.off), hashmap.oid.off); } /* * entry_update -- updates entry in given hashmap with given arguments */ static void entry_update(PMEMobjpool *pop, struct hashmap_rp *hashmap, struct add_entry *args, bool rebuild) { struct entry *entry_p = D_RW(hashmap->entries); entry_p += args->pos; if (rebuild) { entry_p->key = args->data.key; entry_p->value = args->data.value; entry_p->hash = args->data.hash; } else { pmemobj_set_value(pop, args->actv + args->actv_cnt++, &entry_p->key, args->data.key); pmemobj_set_value(pop, args->actv + args->actv_cnt++, &entry_p->value, args->data.value); pmemobj_set_value(pop, args->actv + args->actv_cnt++, &entry_p->hash, args->data.hash); } } /* * entry_add -- increments given hashmap's elements counter and calls * entry_update */ static void entry_add(PMEMobjpool *pop, struct hashmap_rp *hashmap, struct add_entry *args, bool rebuild) { if (rebuild) hashmap->count++; else { pmemobj_set_value(pop, args->actv + args->actv_cnt++, &hashmap->count, hashmap->count + 1); } entry_update(pop, hashmap, args, rebuild); } /* * insert_helper -- inserts specified value into the hashmap * If function was called during rebuild process, no redo logs will be used. * returns: * - 0 if successful, * - -1 on error */ static int insert_helper(PMEMobjpool *pop, struct hashmap_rp *hashmap, uint64_t key, uint64_t value, bool rebuild) { struct pobj_action actv[HASHMAP_RP_MAX_ACTIONS]; struct add_entry args; args.data.key = key; args.data.value = value; args.data.hash = hash(hashmap, key); args.pos = args.data.hash; if (!rebuild) { args.actv = actv; args.actv_cnt = 0; } uint64_t dist = 0; struct entry *entry_p = NULL; for (int n = 0; n < HASHMAP_RP_MAX_SWAPS; ++n) { entry_p = D_RW(hashmap->entries); entry_p += args.pos; /* Case 1: key already exists, override value */ if (!entry_is_empty(entry_p->hash) && entry_p->key == args.data.key) { entry_update(pop, hashmap, &args, rebuild); if (!rebuild) pmemobj_publish(pop, args.actv, args.actv_cnt); return 0; } /* Case 2: slot is empty from the beginning */ if (entry_p->hash == 0) { entry_add(pop, hashmap, &args, rebuild); if (!rebuild) pmemobj_publish(pop, args.actv, args.actv_cnt); return 0; } /* * Case 3: existing element (or tombstone) has probed less than * current element. Swap them (or put into tombstone slot) and * keep going to find another slot for that element. */ uint64_t existing_dist = probe_distance(hashmap, entry_p->hash, args.pos); if (existing_dist < dist) { if (entry_is_deleted(entry_p->hash)) { entry_add(pop, hashmap, &args, rebuild); if (!rebuild) pmemobj_publish(pop, args.actv, args.actv_cnt); return 0; } struct entry temp = *entry_p; entry_update(pop, hashmap, &args, rebuild); args.data = temp; dist = existing_dist; } /* * Case 4: increment slot number and probe counter, keep going * to find free slot */ args.pos = increment_pos(hashmap, args.pos); dist += 1; } LOG("insertion requires too many swaps"); if (!rebuild) pmemobj_cancel(pop, args.actv, args.actv_cnt); return -1; } /* * index_lookup -- checks if given key exists in hashmap. * Returns index number if key was found, 0 otherwise. */ static uint64_t index_lookup(const struct hashmap_rp *hashmap, uint64_t key) { const uint64_t hash_lookup = hash(hashmap, key); uint64_t pos = hash_lookup; uint64_t dist = 0; const struct entry *entry_p = NULL; do { entry_p = D_RO(hashmap->entries); entry_p += pos; if (entry_p->hash == hash_lookup && entry_p->key == key) return pos; pos = increment_pos(hashmap, pos); } while (entry_p->hash != 0 && (dist++) <= probe_distance(hashmap, entry_p->hash, pos) - 1); return 0; } /* * entries_cache -- cache entries from src in entries from dest argument */ static int entries_cache(PMEMobjpool *pop, struct hashmap_rp *dest, const struct hashmap_rp *src) { const struct entry *e_begin = D_RO(src->entries); const struct entry *e_end = e_begin + src->capacity; for (const struct entry *e = e_begin; e != e_end; ++e) { if (entry_is_empty(e->hash)) continue; if (insert_helper(pop, dest, e->key, e->value, true) == -1) return -1; } assert(src->count == dest->count); return 0; } /* * hm_rp_rebuild -- rebuilds the hashmap with a new capacity. * Returns 0 on success, -1 otherwise. */ static int hm_rp_rebuild(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, size_t capacity_new) { /* * We will need 6 actions: * - 1 action to set new capacity * - 1 action to set new resize threshold * - 1 action to alloc memory for new entries * - 1 action to free old entries * - 2 actions to set new oid pointing to new entries */ struct pobj_action actv[6]; size_t actv_cnt = 0; size_t sz_alloc = sizeof(struct entry) * capacity_new; uint64_t resize_threshold_new = static_cast(capacity_new * D_RO(hashmap)->load_factor); pmemobj_set_value(pop, &actv[actv_cnt++], &D_RW(hashmap)->capacity, capacity_new); pmemobj_set_value(pop, &actv[actv_cnt++], &D_RW(hashmap)->resize_threshold, resize_threshold_new); struct hashmap_rp hashmap_rebuild; hashmap_rebuild.count = 0; hashmap_rebuild.capacity = capacity_new; hashmap_rebuild.resize_threshold = resize_threshold_new; hashmap_rebuild.load_factor = D_RO(hashmap)->load_factor; hashmap_rebuild.entries = POBJ_XRESERVE_ALLOC(pop, struct entry, sz_alloc, &actv[actv_cnt], POBJ_XALLOC_ZERO); if (TOID_IS_NULL(hashmap_rebuild.entries)) { LOG(std::string("hashmap alloc failed: ") + pmemobj_errormsg()); goto rebuild_err; } actv_cnt++; if (entries_cache(pop, &hashmap_rebuild, D_RW(hashmap)) == -1) goto rebuild_err; pmemobj_persist(pop, D_RW(hashmap_rebuild.entries), sz_alloc); pmemobj_defer_free(pop, D_RW(hashmap)->entries.oid, &actv[actv_cnt++]); pmemobj_set_value(pop, &actv[actv_cnt++], &D_RW(hashmap)->entries.oid.pool_uuid_lo, hashmap_rebuild.entries.oid.pool_uuid_lo); pmemobj_set_value(pop, &actv[actv_cnt++], &D_RW(hashmap)->entries.oid.off, hashmap_rebuild.entries.oid.off); assert(sizeof(actv) / sizeof(actv[0]) >= actv_cnt); pmemobj_publish(pop, actv, actv_cnt); return 0; rebuild_err: pmemobj_cancel(pop, actv, actv_cnt); return -1; } /* * hm_rp_create -- initializes hashmap state, called after pmemobj_create */ int hm_rp_create(PMEMobjpool *pop, TOID(struct hashmap_rp) * map, std::vector &actv) { hashmap_create(pop, map, actv); return 0; } /* * hm_rp_insert -- rebuilds hashmap if necessary and wraps insert_helper. * returns: * - 0 if successful, * - -1 if something bad happened */ int hm_rp_insert(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, uint64_t key, uint64_t value) { if (D_RO(hashmap)->count + 1 >= D_RO(hashmap)->resize_threshold) { uint64_t capacity_new = D_RO(hashmap)->capacity * 2; if (hm_rp_rebuild(pop, hashmap, capacity_new) != 0) return -1; } return insert_helper(pop, D_RW(hashmap), key, value, false); } /* * hm_rp_remove -- removes specified key from the hashmap, * returns: * - 0 if successful, * - 1 if value didn't exist or if something bad happened */ int hm_rp_remove(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, uint64_t key) { const uint64_t pos = index_lookup(D_RO(hashmap), key); if (pos == 0) return 1; struct entry *entry_p = D_RW(D_RW(hashmap)->entries); entry_p += pos; size_t actvcnt = 0; struct pobj_action actv[5]; pmemobj_set_value(pop, &actv[actvcnt++], &entry_p->hash, entry_p->hash | TOMBSTONE_MASK); pmemobj_set_value(pop, &actv[actvcnt++], &entry_p->value, 0); pmemobj_set_value(pop, &actv[actvcnt++], &entry_p->key, 0); pmemobj_set_value(pop, &actv[actvcnt++], &D_RW(hashmap)->count, D_RW(hashmap)->count - 1); assert(sizeof(actv) / sizeof(actv[0]) >= actvcnt); pmemobj_publish(pop, actv, actvcnt); uint64_t reduced_threshold = static_cast( (static_cast(D_RO(hashmap)->capacity / 2)) * D_RO(hashmap)->load_factor); if (reduced_threshold >= INIT_ENTRIES_NUM_RP && D_RW(hashmap)->count < reduced_threshold && hm_rp_rebuild(pop, hashmap, D_RO(hashmap)->capacity / 2)) return 1; return 0; } /* * hm_rp_get -- checks whether specified key is in the hashmap. */ std::pair hm_rp_get(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, uint64_t key) { struct entry *entry_p = reinterpret_cast( pmemobj_direct(D_RW(hashmap)->entries.oid)); uint64_t pos = index_lookup(D_RO(hashmap), key); return pos == 0 ? std::pair{0, false} : std::pair{(entry_p + pos)->value, true}; } /* * hm_rp_lookup -- checks whether specified key is in the hashmap. * Returns 1 if key was found, 0 otherwise. */ int hm_rp_lookup(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, uint64_t key) { return index_lookup(D_RO(hashmap), key) != 0; } /* * hm_rp_foreach -- calls cb for all values from the hashmap */ int hm_rp_foreach(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap, int (*cb)(const char *key, size_t key_size, const char *value, size_t value_size, void *arg), void *arg) { struct entry *entry_p = reinterpret_cast( pmemobj_direct(D_RO(hashmap)->entries.oid)); int ret = 0; for (size_t i = 0; i < D_RO(hashmap)->capacity; ++i, ++entry_p) { uint64_t hash = entry_p->hash; if (entry_is_empty(hash)) continue; ret = cb(reinterpret_cast(&(entry_p->key)), ENTRY_SIZE, reinterpret_cast(&(entry_p->value)), ENTRY_SIZE, arg); if (ret) return ret; } return 0; } /* * hm_rp_count -- returns number of elements */ size_t hm_rp_count(PMEMobjpool *pop, TOID(struct hashmap_rp) hashmap) { return D_RO(hashmap)->count; } } /* namespace robinhood */ } /* namespace internal */ size_t robinhood::shard_hash(uint64_t key) { return static_cast( fast_hash(ENTRY_SIZE, reinterpret_cast(&key)) & (shards_number - 1)); } robinhood::robinhood(std::unique_ptr cfg) : pmemobj_engine_base(cfg, "pmemkv_robinhood") { Recover(); LOG("Started ok"); } robinhood::~robinhood() { LOG("Stopped ok"); } std::string robinhood::name() { return internal::robinhood::name(); } status robinhood::count_all(std::size_t &cnt) { LOG("count_all"); check_outside_tx(); size_t size = 0; for (size_t i = 0; i < shards_number; ++i) { shared_lock_type lock(mtxs[i]); size += hm_rp_count(pmpool.handle(), container[i]); } cnt = size; return status::OK; } status robinhood::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); for (size_t i = 0; i < shards_number; ++i) { shared_lock_type lock(mtxs[i]); auto ret = hm_rp_foreach(pmpool.handle(), container[i], callback, arg); if (ret) return status::STOPPED_BY_CB; } return status::OK; } status robinhood::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); check_outside_tx(); if (key.size() != ENTRY_SIZE) return status::INVALID_ARGUMENT; auto k = *reinterpret_cast(key.data()); auto shard = shard_hash(k); shared_lock_type lock(mtxs[shard]); return hm_rp_lookup(pmpool.handle(), container[shard], k) == 0 ? status::NOT_FOUND : status::OK; } status robinhood::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); check_outside_tx(); if (key.size() != ENTRY_SIZE) return status::INVALID_ARGUMENT; auto k = *reinterpret_cast(key.data()); auto shard = shard_hash(k); shared_lock_type lock(mtxs[shard]); auto result = hm_rp_get(pmpool.handle(), container[shard], k); lock.unlock(); if (!result.second) { LOG(" key not found"); return status::NOT_FOUND; } callback(reinterpret_cast(&result.first), ENTRY_SIZE, arg); return status::OK; } status robinhood::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(); if (key.size() != ENTRY_SIZE || value.size() != ENTRY_SIZE) return status::INVALID_ARGUMENT; auto k = *reinterpret_cast(key.data()); auto v = *reinterpret_cast(value.data()); auto shard = shard_hash(k); unique_lock_type lock(mtxs[shard]); if (hm_rp_insert(pmpool.handle(), container[shard], k, v) != 0) { // XXX: Extend the C error handling code to pass the actual reason of the // failure. return status::UNKNOWN_ERROR; } return status::OK; } status robinhood::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); check_outside_tx(); if (key.size() != ENTRY_SIZE) return status::INVALID_ARGUMENT; auto k = *reinterpret_cast(key.data()); auto shard = shard_hash(k); unique_lock_type lock(mtxs[shard]); auto result = hm_rp_remove(pmpool.handle(), container[shard], k); if (result == 1) return status::NOT_FOUND; return status::OK; } void robinhood::Recover() { auto sn = std::getenv("PMEMKV_ROBINHOOD_SHARDS_NUMBER"); if (sn) shards_number = std::stoul(sn); else shards_number = SHARDS_DEFAULT; if (!OID_IS_NULL(*root_oid)) { auto pmem_ptr = static_cast( pmemobj_direct(*root_oid)); container = pmem_ptr->map.get(); if (this->shards_number != pmem_ptr->shards_number) throw internal::invalid_argument( "Wrong number of shards set: " + std::to_string(this->shards_number) + ", expected: " + std::to_string(pmem_ptr->shards_number)); } else { auto actv = std::vector(); actv.emplace_back(); auto root = pmemobj_reserve(pmpool.handle(), &actv.back(), sizeof(internal::robinhood::pmem_type), 0); actv.emplace_back(); pmemobj_set_value(pmpool.handle(), &actv.back(), &(root_oid->off), root.off); actv.emplace_back(); pmemobj_set_value(pmpool.handle(), &actv.back(), &(root_oid->pool_uuid_lo), root.pool_uuid_lo); auto pmem_ptr = static_cast( pmemobj_direct(root)); actv.emplace_back(); pmem_ptr->map = pmemobj_reserve( pmpool.handle(), &actv.back(), sizeof(internal::robinhood::hashmap_rp) * shards_number, 0); pmpool.persist(pmem_ptr->map); container = pmem_ptr->map.get(); pmem_ptr->shards_number = this->shards_number; pmpool.persist(pmem_ptr->shards_number); for (size_t i = 0; i < shards_number; ++i) internal::robinhood::hm_rp_create(pmpool.handle(), &container[i], actv); pmemobj_publish(pmpool.handle(), actv.data(), actv.size()); } mtxs = std::vector(shards_number); } static factory_registerer register_robinhood( std::unique_ptr(new robinhood_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-experimental/robinhood.h000066400000000000000000000066741410000423300220620ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #ifndef LIBPMEMKV_ROBINHOOD_H #define LIBPMEMKV_ROBINHOOD_H #include #include #include #include #include #include #include "../comparator/pmemobj_comparator.h" #include "../pmemobj_engine.h" namespace pmem { namespace kv { namespace internal { namespace robinhood { #define SHARDS_DEFAULT 1024 #ifndef HASHMAP_RP_TYPE_OFFSET #define HASHMAP_RP_TYPE_OFFSET 1008 #endif /* Initial number of entries for hashamap_rp */ #define INIT_ENTRIES_NUM_RP 16 /* Load factor to indicate resize threshold */ #define HASHMAP_RP_LOAD_FACTOR 0.5f /* Maximum number of swaps allowed during single insertion */ #define HASHMAP_RP_MAX_SWAPS 150 /* Size of an action array used during single insertion */ #define HASHMAP_RP_MAX_ACTIONS (4 * HASHMAP_RP_MAX_SWAPS + 5) /* Size of a key or value (sizeof(uint64_t)) */ #define ENTRY_SIZE 8 #define TOMBSTONE_MASK (1ULL << 63) /* layout definition */ struct hashmap_rp; TOID_DECLARE(struct hashmap_rp, HASHMAP_RP_TYPE_OFFSET + 0); TOID_DECLARE(struct entry, HASHMAP_RP_TYPE_OFFSET + 1); struct entry { uint64_t key; uint64_t value; uint64_t hash; }; struct add_entry { struct entry data; /* position index in hashmap, where data should be inserted/updated */ size_t pos; /* Action array to perform addition in set of actions */ struct pobj_action *actv; /* Action array index counter */ size_t actv_cnt; }; struct hashmap_rp { /* number of values inserted */ uint64_t count; /* container capacity */ uint64_t capacity; /* resize threshold */ uint64_t resize_threshold; /* load factor to indicate resize threshold */ float load_factor; /* entries */ TOID(struct entry) entries; }; using map_type = hashmap_rp; struct pmem_type { pmem_type() : map() { std::memset(reserved, 0, sizeof(reserved)); } obj::persistent_ptr map; obj::p shards_number; uint64_t reserved[8]; }; } /* namespace robinhood */ } /* namespace internal */ class robinhood : public pmemobj_engine_base { public: robinhood(std::unique_ptr cfg); ~robinhood(); robinhood(const robinhood &) = delete; robinhood &operator=(const robinhood &) = 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; private: using container_type = internal::robinhood::map_type; using mutex_type = std::shared_timed_mutex; using unique_lock_type = std::unique_lock; using shared_lock_type = std::shared_lock; void Recover(); size_t shard_hash(uint64_t key); TOID(struct internal::robinhood::hashmap_rp) * container; std::vector mtxs; size_t shards_number; }; class robinhood_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); return std::unique_ptr(new robinhood(std::move(cfg))); }; std::string get_name() override { return "robinhood"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_ROBINHOOD_H */ pmemkv-1.5.0/src/engines-experimental/stree.cc000066400000000000000000000244761410000423300213570ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #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, "pmemkv_stree"), config(std::move(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(); cnt = my_btree->size(); return status::OK; } template static std::size_t size(It first, It last) { auto dist = std::distance(first, last); assert(dist >= 0); return static_cast(dist); } /* 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(); auto first = my_btree->upper_bound(key); auto last = my_btree->end(); cnt = size(first, last); return status::OK; } /* above or equal to key, key inclusive */ status stree::count_equal_above(string_view key, std::size_t &cnt) { LOG("count_equal_above key>=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = my_btree->lower_bound(key); auto last = my_btree->end(); cnt = size(first, last); 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(); auto first = my_btree->begin(); auto last = my_btree->lower_bound(key); cnt = size(first, last); return status::OK; } /* below or equal to key, key inclusive */ status stree::count_equal_below(string_view key, std::size_t &cnt) { LOG("count_equal_below key>=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = my_btree->begin(); auto last = my_btree->upper_bound(key); cnt = size(first, last); 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(); if (my_btree->key_comp()(key1, key2)) { auto first = my_btree->upper_bound(key1); auto last = my_btree->lower_bound(key2); cnt = size(first, last); } else { cnt = 0; } return status::OK; } status stree::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); check_outside_tx(); auto first = my_btree->begin(); auto last = my_btree->end(); return internal::iterate_through_pairs(first, last, callback, arg); } /* (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(); auto first = my_btree->upper_bound(key); auto last = my_btree->end(); return internal::iterate_through_pairs(first, last, callback, arg); } /* [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(); auto first = my_btree->lower_bound(key); auto last = my_btree->end(); return internal::iterate_through_pairs(first, last, callback, arg); } /* [start, key], below or equal to key */ status stree::get_equal_below(string_view key, get_kv_callback *callback, void *arg) { LOG("get_equal_below start key>=" << std::string(key.data(), key.size())); check_outside_tx(); auto first = my_btree->begin(); auto last = my_btree->upper_bound(key); return internal::iterate_through_pairs(first, last, callback, arg); } /* [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 first = my_btree->begin(); auto last = my_btree->lower_bound(key); return internal::iterate_through_pairs(first, last, callback, arg); } /* 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(); if (my_btree->key_comp()(key1, key2)) { auto first = my_btree->upper_bound(key1); auto last = my_btree->lower_bound(key2); return internal::iterate_through_pairs(first, last, callback, arg); } 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(key); 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(key); 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->try_emplace(key, value); if (!result.second) { // key already exists, so update typename internal::stree::btree_type::value_type &entry = *result.first; transaction::manual tx(pmpool); entry.second = value; 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(key); 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->key_comp().runtime_initialize( internal::extract_comparator(*config)); } else { pmem::obj::transaction::run(pmpool, [&] { pmem::obj::transaction::snapshot(root_oid); *root_oid = pmem::obj::make_persistent() .raw(); my_btree = (internal::stree::btree_type *)pmemobj_direct(*root_oid); my_btree->key_comp().initialize( internal::extract_comparator(*config)); }); } } internal::iterator_base *stree::new_iterator() { return new stree_iterator{my_btree}; } internal::iterator_base *stree::new_const_iterator() { return new stree_iterator{my_btree}; } stree::stree_iterator::stree_iterator(container_type *c) : container(c), it_(nullptr), pop(pmem::obj::pool_by_vptr(c)) { } stree::stree_iterator::stree_iterator(container_type *c) : stree::stree_iterator(c) { } status stree::stree_iterator::seek(string_view key) { init_seek(); it_ = container->find(key); if (it_ != container->end()) return status::OK; return status::NOT_FOUND; } status stree::stree_iterator::seek_lower(string_view key) { init_seek(); it_ = container->lower_bound(key); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status stree::stree_iterator::seek_lower_eq(string_view key) { init_seek(); it_ = container->upper_bound(key); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status stree::stree_iterator::seek_higher(string_view key) { init_seek(); it_ = container->upper_bound(key); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status stree::stree_iterator::seek_higher_eq(string_view key) { init_seek(); it_ = container->lower_bound(key); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status stree::stree_iterator::seek_to_first() { init_seek(); if (container->size() == 0) return status::NOT_FOUND; it_ = container->begin(); return status::OK; } status stree::stree_iterator::seek_to_last() { init_seek(); if (container->size() == 0) return status::NOT_FOUND; it_ = container->end(); --it_; return status::OK; } status stree::stree_iterator::is_next() { auto tmp = it_; if (tmp == container->end() || ++tmp == container->end()) return status::NOT_FOUND; return status::OK; } status stree::stree_iterator::next() { init_seek(); if (it_ == container->end() || ++it_ == container->end()) return status::NOT_FOUND; return status::OK; } status stree::stree_iterator::prev() { init_seek(); if (it_ == container->begin()) return status::NOT_FOUND; --it_; return status::OK; } result stree::stree_iterator::key() { assert(it_ != container->end()); return string_view(it_->first.cdata(), it_->first.length()); } result> stree::stree_iterator::read_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.size() || pos + n < pos) n = it_->second.size() - pos; return {it_->second.crange(pos, n)}; } result> stree::stree_iterator::write_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.size() || pos + n < pos) n = it_->second.size() - pos; log.push_back({{it_->second.cdata() + pos, n}, pos}); auto &val = log.back().first; return {{&val[0], &val[0] + n}}; } status stree::stree_iterator::commit() { pmem::obj::transaction::run(pop, [&] { for (auto &p : log) { auto dest = it_->second.range(p.second, p.first.size()); std::copy(p.first.begin(), p.first.end(), dest.begin()); } }); log.clear(); return status::OK; } void stree::stree_iterator::abort() { log.clear(); } static factory_registerer register_stree(std::unique_ptr(new stree_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-experimental/stree.h000066400000000000000000000101371410000423300212060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_STREE_H #define LIBPMEMKV_STREE_H #include #include #include #include "../comparator/pmemobj_comparator.h" #include "../iterator.h" #include "../pmemobj_engine.h" #include "stree/persistent_b_tree.h" using pmem::obj::persistent_ptr; using pmem::obj::pool; namespace pmem { namespace kv { namespace internal { namespace stree { /** * Indicates the maximum number of descendants a single node can have. * DEGREE - 1 is the maximum number of entries a node can have. */ const size_t DEGREE = 32; using string_t = pmem::obj::string; using key_type = string_t; using value_type = string_t; using btree_type = b_tree; } /* namespace stree */ } /* namespace internal */ class stree : public pmemobj_engine_base { private: using container_type = internal::stree::btree_type; using container_iterator = typename container_type::iterator; template class stree_iterator; 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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; private: stree(const stree &); void operator=(const stree &); void Recover(); internal::stree::btree_type *my_btree; std::unique_ptr config; }; template <> class stree::stree_iterator : public internal::iterator_base { using container_type = stree::container_type; public: stree_iterator(container_type *container); status seek(string_view key) final; status seek_lower(string_view key) final; status seek_lower_eq(string_view key) final; status seek_higher(string_view key) final; status seek_higher_eq(string_view key) final; status seek_to_first() final; status seek_to_last() final; status is_next() final; status next() final; status prev() final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; container_type::iterator it_; pmem::obj::pool_base pop; }; template <> class stree::stree_iterator : public stree::stree_iterator { using container_type = stree::container_type; public: stree_iterator(container_type *container); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; }; class stree_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); return std::unique_ptr(new stree(std::move(cfg))); }; std::string get_name() override { return "stree"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_STREE_H */ pmemkv-1.5.0/src/engines-experimental/stree/000077500000000000000000000000001410000423300210335ustar00rootroot00000000000000pmemkv-1.5.0/src/engines-experimental/stree/persistent_b_tree.h000066400000000000000000002127211410000423300247310ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef PERSISTENT_B_TREE #define PERSISTENT_B_TREE #include #include #include #include #include #include #include #include #include #include namespace pmem { namespace kv { namespace internal { using namespace pmem::obj; /** * Base node type for inner and leaf node types */ class node_t { public: node_t(uint64_t level = 0) : _level(level) { } bool leaf() const { return _level == 0ull; } uint64_t level() const { return _level; } private: uint64_t _level; }; /* class node_t */ /** * Implements iteration over a single tree node */ template class node_iterator { private: using leaf_type = LeafType; using leaf_node_ptr = typename std::conditional::type; friend class node_iterator; public: using value_type = typename leaf_type::value_type; using iterator_category = std::random_access_iterator_tag; using size_type = typename leaf_type::size_type; using difference_type = typename leaf_type::difference_type; using reference = typename std::conditional::type; using pointer = typename std::conditional::type; node_iterator(); node_iterator(leaf_node_ptr node_ptr, size_type p); node_iterator(const node_iterator &other); template ::type> node_iterator(const node_iterator &other); node_iterator &operator=(const node_iterator &other); node_iterator &operator++(); node_iterator operator++(int); node_iterator &operator--(); node_iterator operator--(int); node_iterator operator+(size_type off) const; node_iterator operator+=(difference_type off); node_iterator operator-(difference_type off) const; difference_type operator-(const node_iterator &other) const; bool operator==(const node_iterator &other) const; bool operator!=(const node_iterator &other) const; bool operator<(const node_iterator &other) const; bool operator>(const node_iterator &other) const; reference operator*() const; pointer operator->() const; private: leaf_node_ptr node; size_type position; }; /* class node_iterator */ template class leaf_node_t : public node_t { public: using key_type = Key; using mapped_type = T; using key_compare = Compare; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::pair; using reference = value_type &; using const_reference = const value_type &; using pointer = value_type *; using const_pointer = const value_type *; using iterator = node_iterator; using const_iterator = node_iterator; leaf_node_t(); ~leaf_node_t(); void move(pool_base &pop, persistent_ptr other, const key_compare &); template iterator insert(iterator idxs_pos, K &&key, M &&obj); template iterator find(const K &key, const key_compare &); template const_iterator find(const K &key, const key_compare &) const; template iterator lower_bound(const K &key, const key_compare &comp); template size_type erase(pool_base &pop, const K &key, const key_compare &); iterator begin(); const_iterator begin() const; const_iterator cbegin() const; iterator end(); const_iterator end() const; const_iterator cend() const; size_type size() const; bool full() const; const_reference front() const; const_reference back() const; reference operator[](size_type pos); const_reference operator[](size_type pos) const; const persistent_ptr &get_next() const; void set_next(const persistent_ptr &n); const persistent_ptr &get_prev() const; void set_prev(const persistent_ptr &p); private: /* uninitialized static array of value_type is used to avoid entries * default initialization and to avoid additional allocations */ union { value_type entries[capacity]; }; /* array of indexes to support ordering */ pmem::obj::array idxs; pmem::obj::p _size; /* persistent pointers to the neighboring leafs */ pmem::obj::persistent_ptr prev; pmem::obj::persistent_ptr next; /* private helper methods */ template pointer emplace(difference_type pos, Args &&... args); size_type insert_idx(const_iterator pos); void remove_idx(size_type idx); void internal_erase(pool_base &pop, iterator it); bool is_sorted(const key_compare &); void add_to_tx(size_type begin, size_type end); }; /* class leaf_node_t */ template class inner_node_t : public node_t { private: using self_type = inner_node_t; using node_pptr = pmem::obj::persistent_ptr; using key_pptr = pmem::obj::persistent_ptr; public: using key_type = Key; using value_type = key_type; using key_compare = Compare; using reference = key_type &; using const_reference = const key_type &; using pointer = key_type *; using const_pointer = const key_type *; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using iterator = node_iterator; using const_iterator = node_iterator; inner_node_t(size_type level); inner_node_t(size_type level, const_reference key, const node_pptr &first_child, const node_pptr &second_child); ~inner_node_t(); iterator move(pool_base &pop, inner_node_t &other, key_pptr &partition_key); template void replace(iterator it, const K &key); void delete_with_child(iterator it, bool left); void inherit_child(iterator it, node_pptr &child, bool left); void update_splitted_child(pool_base &pop, const_reference key, node_pptr &left_child, node_pptr &right_child, const key_compare &); template std::tuple get_child_and_siblings(const K &key, const key_compare &); template const node_pptr &get_child(const K &key, const key_compare &) const; const node_pptr &get_child(const_reference key, const key_compare &) const; const node_pptr &get_left_child(const_iterator it) const; const node_pptr &get_right_child(const_iterator it) const; bool full() const; iterator begin(); const_iterator begin() const; const_iterator cbegin() const; iterator end(); const_iterator end() const; const_iterator cend() const; size_type size() const; const_reference back() const; reference operator[](size_type pos); const_reference operator[](size_type pos) const; private: key_pptr entries[capacity]; node_pptr children[capacity + 1]; pmem::obj::p _size = 0; pool_base get_pool() const noexcept; bool is_sorted(const key_compare &); }; /* class inner_node_t */ template class b_tree_iterator { private: using leaf_type = LeafType; using leaf_node_ptr = typename std::conditional::type; using leaf_iterator = typename std::conditional::type; friend class b_tree_iterator; public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = ptrdiff_t; using value_type = typename leaf_iterator::value_type; using reference = typename leaf_iterator::reference; using pointer = typename leaf_iterator::pointer; b_tree_iterator(std::nullptr_t); b_tree_iterator(leaf_node_ptr node); b_tree_iterator(leaf_node_ptr node, leaf_iterator _leaf_it); b_tree_iterator(const b_tree_iterator &other); template ::type> b_tree_iterator(const b_tree_iterator &other); b_tree_iterator &operator=(const b_tree_iterator &other); b_tree_iterator &operator++(); b_tree_iterator operator++(int); b_tree_iterator &operator--(); b_tree_iterator operator--(int); bool operator==(const b_tree_iterator &other) const; bool operator!=(const b_tree_iterator &other) const; reference operator*() const; pointer operator->() const; private: leaf_node_ptr current_node; leaf_iterator leaf_it; }; /* class b_tree_iterator */ template class b_tree_base { private: const static std::size_t node_capacity = degree - 1; using self_type = b_tree_base; using leaf_type = leaf_node_t; using inner_type = inner_node_t; using key_pptr = persistent_ptr; using node_pptr = persistent_ptr; using leaf_pptr = persistent_ptr; using inner_pptr = persistent_ptr; using path_type = std::vector; using inner_pair = std::pair; public: using value_type = typename leaf_type::value_type; using key_type = typename leaf_type::key_type; using mapped_type = typename leaf_type::mapped_type; using key_compare = Compare; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = typename leaf_type::reference; using const_reference = typename leaf_type::const_reference; using pointer = typename leaf_type::pointer; using const_pointer = typename leaf_type::const_pointer; using iterator = b_tree_iterator; using const_iterator = b_tree_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; b_tree_base(); ~b_tree_base(); template std::pair try_emplace(K &&key, M &&obj); template iterator find(const K &key); template const_iterator find(const K &key) const; template iterator lower_bound(const K &key); template const_iterator lower_bound(const K &key) const; template iterator upper_bound(const K &key); template const_iterator upper_bound(const K &key) const; template size_type erase(const K &key); iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; reverse_iterator rbegin(); reverse_iterator rend(); size_type size() const noexcept; reference operator[](size_type pos); const_reference operator[](size_type pos) const; key_compare &key_comp(); const key_compare &key_comp() const; private: node_pptr root; node_pptr split_node; node_pptr left_child; node_pptr right_child; key_compare compare; pmem::obj::p _size; const key_type &get_last_key(const node_pptr &node); leaf_type *leftmost_leaf() const; leaf_type *rightmost_leaf() const; void create_new_root(const key_type &, node_pptr &, node_pptr &); typename inner_type::const_iterator split_half(pool_base &pop, inner_pptr &node, inner_pptr &other, key_pptr &partition_key); void split_inner_node(pool_base &pop, inner_pptr &src_node); void split_inner_node(pool_base &pop, inner_pptr &src_node, inner_type *parent_node); template std::pair split_leaf_node(pool_base &pop, leaf_pptr &split_leaf, K &&key, M &&obj); template std::pair split_leaf_node(pool_base &pop, inner_type *parent_node, leaf_pptr &split_leaf, K &&key, M &&obj); leaf_type *find_leaf_node(const key_type &key) const; template leaf_type *find_leaf_node(const K &key) const; template leaf_pptr find_leaf_to_insert(const K &key, path_type &path) const; typename path_type::const_iterator find_full_node(const path_type &path); template std::pair internal_insert(leaf_pptr leaf, K &&key, M &&obj); template leaf_pptr get_path_ext(const K &key, std::vector &path, std::vector> &neighbors, inner_pair &inner_ptr); const_reference get_suitable_entry(inner_pair &node); void delete_leaf_ext(leaf_pptr &leaf, inner_pair &parent, bool has_left_sibling); void delete_inner_ext(inner_pptr &node, inner_pair &parent, std::pair &neighbors, bool has_left_sibling); static inner_pptr &cast_inner(node_pptr &node); static inner_type *cast_inner(node_t *node); static leaf_pptr &cast_leaf(node_pptr &node); static leaf_type *cast_leaf(node_t *node); static node_pptr &cast_node(leaf_pptr &node); static node_pptr &cast_node(inner_pptr &node); template inline inner_pptr allocate_inner(Args &&... args); template inline leaf_pptr allocate_leaf(Args &&... args); inline void deallocate(node_pptr &node); inline void deallocate(leaf_pptr &node); inline void deallocate(inner_pptr &node); PMEMobjpool *get_objpool(); pool_base get_pool_base(); }; /* class b_tree_base */ // ------------------------------------------------------------------------------------- // ------------------------------------- node_iterator --------------------------------- // ------------------------------------------------------------------------------------- template node_iterator::node_iterator() : node(nullptr), position(0) { } template node_iterator::node_iterator(leaf_node_ptr node_ptr, size_type p) : node(node_ptr), position(p) { } template node_iterator::node_iterator(const node_iterator &other) : node(other.node), position(other.position) { } template template node_iterator::node_iterator( const node_iterator &other) : node(other.node), position(other.position) { } template node_iterator & node_iterator::operator=(const node_iterator &other) { if (this == &other) return *this; node = other.node; position = other.position; return *this; } template node_iterator &node_iterator::operator++() { ++position; return *this; } template node_iterator node_iterator::operator++(int) { node_iterator tmp = *this; ++*this; return tmp; } template node_iterator &node_iterator::operator--() { assert(position > 0); --position; return *this; } template node_iterator node_iterator::operator--(int) { node_iterator tmp = *this; --*this; return tmp; } template node_iterator node_iterator::operator+(size_type off) const { return node_iterator(node, position + off); } template node_iterator node_iterator::operator+=(difference_type off) { position += static_cast(off); return *this; } template node_iterator node_iterator::operator-(difference_type off) const { assert(node != nullptr); assert(position >= static_cast(off)); return node_iterator(node, position - static_cast(off)); } template typename node_iterator::difference_type node_iterator::operator-(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return static_cast(position - other.position); } template bool node_iterator::operator==(const node_iterator &other) const { return node == other.node && position == other.position; } template bool node_iterator::operator!=(const node_iterator &other) const { return !(*this == other); } template bool node_iterator::operator<(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return position < other.position; } template bool node_iterator::operator>(const node_iterator &other) const { assert(node != nullptr); assert(other.node != nullptr); assert(node == other.node); return position > other.position; } template typename node_iterator::reference node_iterator::operator*() const { assert(node != nullptr); return node->operator[](position); } template typename node_iterator::pointer node_iterator::operator->() const { return &**this; } // ------------------------------------------------------------------------------------- // ------------------------------------- leaf_node_t ----------------------------------- // ------------------------------------------------------------------------------------- template leaf_node_t::leaf_node_t() : node_t() { assert(pmemobj_tx_stage() == TX_STAGE_WORK); std::iota(idxs.begin(), idxs.end(), 0); _size = 0; } template leaf_node_t::~leaf_node_t() { assert(pmemobj_tx_stage() == TX_STAGE_WORK); try { for (value_type &e : *this) { e.first.~key_type(); e.second.~mapped_type(); } } catch (transaction_error &e) { std::terminate(); } } /** * Moves second half of the 'other' to 'this' in sorted order. * Inserts given key-object as a new entry to an appropriate node. * * @pre std:distance(middle, end) > 0 * * @post this = other[middle, end) * @post other = other[0, middle) * * @return iterator on newly inserted entry */ template void leaf_node_t::move(pool_base &pop, persistent_ptr other, const key_compare &comp) { assert(other->full()); assert(this->size() == 0); size_type middle_idx = other->size() / 2; auto middle = std::make_move_iterator(other->begin() + middle_idx); auto last = std::make_move_iterator(other->end()); auto temp = middle; /* move second half from 'other' to 'this' */ pmem::obj::transaction::run(pop, [&] { /* add range to tx before moving to avoid sequential snapshotting */ other->add_to_tx(middle_idx, other->size()); difference_type count = 0; while (temp != last) { emplace(count++, *temp++); } _size = static_cast(count); other->_size -= static_cast(count); }); assert(std::distance(begin(), end()) > 0); assert(is_sorted(comp)); } template template typename leaf_node_t::iterator leaf_node_t::lower_bound(const K &key, const key_compare &comp) { return std::lower_bound( begin(), end(), key, [&comp](const_reference e, const K &key) { return comp(e.first, key); }); } /** * Inserts element into the leaf in a sorted way specified by idxs_pos. * * @pre key must not already exist in the leaf. */ template template typename leaf_node_t::iterator leaf_node_t::insert(iterator idxs_pos, K &&key, M &&obj) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(!full()); difference_type insert_pos = idxs[size()]; assert(std::none_of( idxs.cdata(), idxs.cdata() + size(), [&insert_pos](difference_type idx) { return insert_pos == idx; })); // insert an entry to the end emplace(insert_pos, std::forward(key), std::forward(obj)); // update idxs & return iterator return iterator(this, insert_idx(idxs_pos)); } template template typename leaf_node_t::iterator leaf_node_t::find(const K &key, const key_compare &comp) { iterator it = lower_bound(key, comp); if (it != end() && (!comp(it->first, key) && !comp(key, it->first))) { return it; } else { return end(); } } template template typename leaf_node_t::const_iterator leaf_node_t::find(const K &key, const key_compare &comp) const { const_iterator it = lower_bound(key, comp); if (it != cend() && (!comp(it->first, key) && !comp(key, it->first))) { return it; } else { return cend(); } } template template typename leaf_node_t::size_type leaf_node_t::erase(pool_base &pop, const K &key, const key_compare &comp) { iterator it = find(key, comp); if (it == end()) return size_type(0); internal_erase(pop, it); assert(is_sorted(comp)); return size_type(1); } /** * Return begin iterator on an array of correct indices. */ template typename leaf_node_t::iterator leaf_node_t::begin() { return iterator(this, 0); } /** * Return const_iterator to the beginning. */ template typename leaf_node_t::const_iterator leaf_node_t::begin() const { return const_iterator(this, 0); } /** * Return const_iterator to the beginning. */ template typename leaf_node_t::const_iterator leaf_node_t::cbegin() const { return const_iterator(this, 0); } /** * Return end iterator on an array of indices. */ template typename leaf_node_t::iterator leaf_node_t::end() { return iterator(this, size()); } /** * Return const_iterator to the end. */ template typename leaf_node_t::const_iterator leaf_node_t::end() const { return const_iterator(this, size()); } /** * Return const_iterator to the end. */ template typename leaf_node_t::const_iterator leaf_node_t::cend() const { return const_iterator(this, size()); } /** * Return the size of the array of entries (key/value). */ template typename leaf_node_t::size_type leaf_node_t::size() const { return _size; } template bool leaf_node_t::full() const { return size() == capacity; } template typename leaf_node_t::const_reference leaf_node_t::front() const { return entries[idxs[0]]; } template typename leaf_node_t::const_reference leaf_node_t::back() const { return entries[idxs[size() - 1]]; } template typename leaf_node_t::reference leaf_node_t::operator[](size_type pos) { assert(pos <= size()); return entries[idxs[pos]]; } template typename leaf_node_t::const_reference leaf_node_t::operator[](size_type pos) const { assert(pos <= size()); return entries[idxs[pos]]; } template const persistent_ptr> & leaf_node_t::get_next() const { return this->next; } template void leaf_node_t::set_next( const persistent_ptr &n) { this->next = n; } template const persistent_ptr> & leaf_node_t::get_prev() const { return this->prev; } template void leaf_node_t::set_prev( const persistent_ptr &p) { this->prev = p; } /** * Constructs value_type in position 'pos' of entries with arguments 'args'. * * @pre must be called in a transaction scope. */ template template typename leaf_node_t::pointer leaf_node_t::emplace(difference_type pos, Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); /* to avoid snapshotting of an uninitialized memory */ pmemobj_tx_xadd_range_direct(entries + pos, sizeof(value_type), POBJ_XADD_NO_SNAPSHOT); return new (entries + pos) value_type(std::forward(args)...); } /** * Replaces index of newly allocated on position idxs[size()] element in sorted order. * * @param pos - position in sorted idxs array where entry must reside. * * @pre must be used right after addition of a new entry * @pre new entry must be added into idxs[size()] position */ template typename leaf_node_t::size_type leaf_node_t::insert_idx(const_iterator pos) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); difference_type new_entry_idx = idxs[size()]; auto idx_pos = static_cast(std::distance(cbegin(), pos)); auto slice = idxs.range(idx_pos, size() - idx_pos); auto to_insert = std::copy_backward(slice.begin(), slice.end(), slice.end() + 1); *(--to_insert) = new_entry_idx; ++_size; size_type result = static_cast(to_insert - idxs.cbegin()); assert(result >= 0); return result; } template void leaf_node_t::remove_idx(size_type idx) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(size() > 0); difference_type to_replace = idxs[idx]; auto slice = idxs.range(idx, size() - idx); auto replace_pos = std::copy(slice.begin() + 1, slice.end(), slice.begin()); *replace_pos = to_replace; --_size; } /** * Remove element pointed by iterator. */ template void leaf_node_t::internal_erase(pool_base &pop, iterator it) { size_type idx = static_cast(std::distance(begin(), it)); pmem::obj::transaction::run(pop, [&] { /* destruct key-value pair */ (*it).first.~key_type(); (*it).second.~mapped_type(); /* update idxs */ remove_idx(idx); }); } template bool leaf_node_t::is_sorted(const key_compare &comp) { return std::is_sorted(begin(), end(), [&comp](const_reference a, const_reference b) { return comp(a.first, b.first); }); } /** * Adds the range [begin, end) of underlying array to transaction. */ template void leaf_node_t::add_to_tx(size_type begin, size_type end) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); pmemobj_tx_xadd_range_direct(entries + begin, sizeof(value_type) * (end - begin), POBJ_XADD_ASSUME_INITIALIZED); } // ------------------------------------------------------------------------------------- // ------------------------------------- inner_node_t ---------------------------------- // ------------------------------------------------------------------------------------- template inner_node_t::inner_node_t(size_type level) : node_t(level), _size(0) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); } template inner_node_t::inner_node_t(size_type level, const_reference key, const node_pptr &first_child, const node_pptr &second_child) : node_t(level) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); entries[0] = pmem::obj::persistent_ptr(&key); children[0] = first_child; children[1] = second_child; _size = 1; } template inner_node_t::~inner_node_t() { } /** * Moves second half from 'other' to 'this'. * Returns iterator to first from 'this'. */ template typename inner_node_t::iterator inner_node_t::move(pool_base &pop, inner_node_t &other, key_pptr &partition_key) { assert(size() == 0); assert(other.size() > size_type(1)); key_pptr *middle = other.entries + other.size() / 2; key_pptr *last = other.entries + other.size(); size_type new_size = static_cast(std::distance(middle + 1, last)); node_pptr *middle_child = other.children + (other.size() / 2) + 1; node_pptr *last_child = other.children + other.size() + 1; /* move second half from 'other' to 'this' */ pmem::obj::transaction::run(pop, [&] { /* save partition key */ partition_key = *middle; std::move(middle + 1, last, entries); std::move(middle_child, last_child, children); _size = new_size; other._size -= (new_size + 1); }); assert(std::distance(begin(), end()) > 0); return begin(); } /** * Changes entry specified by the iterator with key pointer */ template template void inner_node_t::replace(iterator it, const K &key) { size_type pos = static_cast(std::distance(begin(), it)); entries[pos] = pmem::obj::persistent_ptr(&key); } /** * Updates inner node after splitting child. * * @param[in] pop - persistent pool * @param[in] key - key of the first entry in right_child * @param[in] left_child - new child node that must be linked * @param[in] right_child - new child node that must be linked */ template void inner_node_t::update_splitted_child(pool_base &pop, const_reference key, node_pptr &left_child, node_pptr &right_child, const key_compare &comp) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(!full()); const_iterator insert_it = std::lower_bound( cbegin(), cend(), key, [&comp](const_reference lhs, const_reference rhs) { return comp(lhs, rhs); }); difference_type insert_idx = std::distance(cbegin(), insert_it); /* update entries inserting new key */ key_pptr *to_insert = std::copy_backward(entries + insert_idx, entries + size(), entries + size() + 1); assert(insert_idx < std::distance(entries, to_insert)); *(--to_insert) = pmem::obj::persistent_ptr(&key); ++_size; /* update children inserting new descendants */ node_pptr *to_insert_child = std::copy_backward( children + insert_idx + 1, children + size(), children + size() + 1); *(--to_insert_child) = right_child; *(--to_insert_child) = left_child; assert(is_sorted(comp)); } /** * Deletes key specified by iterator. * Must be followed by node balancing. */ template void inner_node_t::delete_with_child(iterator it, bool left) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(size() > 0); size_type pos = static_cast(std::distance(begin(), it)); std::move(entries + pos + 1, entries + size(), entries + pos); if (left) { std::move(children + pos + 1, children + size() + 1, children + pos); } else { std::move(children + pos + 2, children + size() + 1, children + pos + 1); } --_size; } /** * Inherits child specified by iterator and 'left' bool. * Key is updated by smallest in right subtree. * Assuming that previous child is no longer used and must be deleted. * * @pre child.size() == 0 */ template void inner_node_t::inherit_child(iterator it, node_pptr &child, bool left) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(size() > 0); size_type pos = static_cast(std::distance(begin(), it)); if (left) { children[pos] = child; } else { children[pos + 1] = child; } } template template const typename inner_node_t::node_pptr & inner_node_t::get_child(const K &key, const key_compare &comp) const { const_iterator it = std::upper_bound( cbegin(), cend(), key, [&comp](const K &lhs, const_reference rhs) { return comp(lhs, rhs); }); return get_left_child(it); } template template std::tuple::iterator> inner_node_t::get_child_and_siblings(const K &key, const key_compare &comp) { assert(size() > 0); iterator it = std::upper_bound( begin(), end(), key, [&comp](const K &lhs, const_reference rhs) { return comp(lhs, rhs); }); if (it == begin()) { return std::make_tuple(get_left_child(it).get(), nullptr, get_right_child(it).get(), it); } else if (it == end()) { return std::make_tuple(get_left_child(it).get(), get_left_child(it - 1).get(), nullptr, it - 1); } else { return std::make_tuple(get_left_child(it).get(), get_left_child(it - 1).get(), get_right_child(it).get(), it - 1); } } template const typename inner_node_t::node_pptr & inner_node_t::get_child(const_reference key, const key_compare &comp) const { const_iterator it = std::upper_bound( cbegin(), cend(), key, [&comp](const_reference lhs, const_reference rhs) { return comp(lhs, rhs); }); return get_left_child(it); } template const typename inner_node_t::node_pptr & inner_node_t::get_left_child(const_iterator it) const { auto result = std::distance(begin(), it); assert(result >= 0); size_type child_pos = static_cast(result); return children[child_pos]; } template const typename inner_node_t::node_pptr & inner_node_t::get_right_child(const_iterator it) const { auto result = std::distance(begin(), it); assert(result >= 0); size_type child_pos = static_cast(result + 1); return children[child_pos]; } template bool inner_node_t::full() const { return this->size() == capacity; } /** * Return begin iterator on an array of keys. */ template typename inner_node_t::iterator inner_node_t::begin() { return iterator(this, 0); } /** * Return begin const_iterator on an array of keys. */ template typename inner_node_t::const_iterator inner_node_t::begin() const { return const_iterator(this, 0); } /** * Return begin const_iterator on an array of keys. */ template typename inner_node_t::const_iterator inner_node_t::cbegin() const { return const_iterator(this, 0); } /** * Return end iterator on an array of keys. */ template typename inner_node_t::iterator inner_node_t::end() { return begin() + this->size(); } /** * Return end const_iterator on an array of keys. */ template typename inner_node_t::const_iterator inner_node_t::end() const { return begin() + this->size(); } /** * Return end const_iterator on an array of keys. */ template typename inner_node_t::const_iterator inner_node_t::cend() const { return begin() + this->size(); } /** * Return the size of the array of keys. */ template typename inner_node_t::size_type inner_node_t::size() const { return _size; } template typename inner_node_t::const_reference inner_node_t::back() const { return *entries[this->size() - 1]; } template typename inner_node_t::reference inner_node_t::operator[](size_type pos) { assert(pos <= size()); return *entries[pos]; } template typename inner_node_t::const_reference inner_node_t::operator[](size_type pos) const { assert(pos <= size()); return *entries[pos]; } template bool inner_node_t::is_sorted(const key_compare &comp) { return std::is_sorted(begin(), end(), [&comp](const key_type &lhs, const key_type &rhs) { return comp(lhs, rhs); }); } template pool_base inner_node_t::get_pool() const noexcept { auto pop = pmemobj_pool_by_ptr(this); assert(pop != nullptr); return pool_base(pop); } // ------------------------------------------------------------------------------------- // ----------------------------------- b_tree_iterator --------------------------------- // ------------------------------------------------------------------------------------- template b_tree_iterator::b_tree_iterator(std::nullptr_t) : current_node(nullptr), leaf_it() { } template b_tree_iterator::b_tree_iterator(leaf_node_ptr node) : current_node(node), leaf_it(node->begin()) { } template b_tree_iterator::b_tree_iterator(leaf_node_ptr node, leaf_iterator _leaf_it) : current_node(node), leaf_it(_leaf_it) { } template b_tree_iterator::b_tree_iterator(const b_tree_iterator &other) : current_node(other.current_node), leaf_it(other.leaf_it) { } template template b_tree_iterator::b_tree_iterator( const b_tree_iterator &other) : current_node(other.current_node), leaf_it(other.leaf_it) { } template b_tree_iterator & b_tree_iterator::operator=(const b_tree_iterator &other) { current_node = other.current_node; leaf_it = other.leaf_it; return *this; } template b_tree_iterator &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; } template b_tree_iterator b_tree_iterator::operator++(int) { b_tree_iterator tmp = *this; ++*this; return tmp; } template b_tree_iterator &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->end(); } } else { --leaf_it; } return *this; } template b_tree_iterator b_tree_iterator::operator--(int) { b_tree_iterator tmp = *this; --*this; return tmp; } template bool b_tree_iterator::operator==(const b_tree_iterator &other) const { return current_node == other.current_node && leaf_it == other.leaf_it; } template bool b_tree_iterator::operator!=(const b_tree_iterator &other) const { return !(*this == other); } template typename b_tree_iterator::reference b_tree_iterator::operator*() const { return *(leaf_it); } template typename b_tree_iterator::pointer b_tree_iterator::operator->() const { return &**this; } // ------------------------------------------------------------------------------------- // ------------------------------------- b_tree_base ----------------------------------- // ------------------------------------------------------------------------------------- template b_tree_base::b_tree_base() { assert(pmemobj_tx_stage() == TX_STAGE_WORK); cast_leaf(root) = allocate_leaf(); _size = 0; } template b_tree_base::~b_tree_base() { try { deallocate(root); } catch (transaction_error &e) { std::terminate(); } } template template std::pair::iterator, bool> b_tree_base::try_emplace(K &&key, M &&obj) { auto pop = get_pool_base(); path_type path; leaf_pptr leaf = find_leaf_to_insert(std::forward(key), path); // --------------- entry with the same key found --------------- typename leaf_type::iterator leaf_it = leaf->find(std::forward(key), compare); if (leaf_it != leaf->end()) { return std::pair(iterator(leaf.get(), leaf_it), false); } // ------------------ leaf not full -> insert ------------------ if (!leaf->full()) { return internal_insert(leaf, std::forward(key), std::forward(obj)); } // -------------------- if root is leaf ------------------------ if (path.empty()) { return split_leaf_node(pop, leaf, std::forward(key), std::forward(obj)); } // ---------- find the first not full node from leaf ----------- auto i = path.end() - 1; for (; i > path.begin(); --i) { if (!(*i)->full()) { break; } } // -------------- if root is full split root ------------------- inner_type *parent_node = nullptr; if ((*i)->full()) { split_inner_node(pop, *i); parent_node = cast_inner( cast_inner(root)->get_child(std::forward(key), compare).get()); } else { parent_node = (*i).get(); } ++i; for (; i != path.end(); ++i) { split_inner_node(pop, *i, parent_node); parent_node = cast_inner( parent_node->get_child(std::forward(key), compare).get()); } return split_leaf_node(pop, parent_node, leaf, std::forward(key), std::forward(obj)); } template template typename b_tree_base::iterator b_tree_base::find(const K &key) { leaf_type *leaf = find_leaf_node(key); typename leaf_type::iterator leaf_it = leaf->find(key, compare); if (leaf->end() == leaf_it) return end(); return iterator(leaf, leaf_it); } template template typename b_tree_base::const_iterator b_tree_base::find(const K &key) const { leaf_type *leaf = find_leaf_node(key); typename leaf_type::const_iterator leaf_it = leaf->find(key); if (leaf->cend() == leaf_it) return cend(); 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 binary order (see * std::string::compare). * * @param[in] key sets the lower bound (inclusive) * * @return iterator */ template template typename b_tree_base::iterator b_tree_base::lower_bound(const K &key) { leaf_type *leaf = find_leaf_node(key); typename leaf_type::iterator leaf_it = std::lower_bound( leaf->begin(), leaf->end(), key, [this](const_reference e, const K &key) { return compare(e.first, 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 binary order (see * std::string::compare). * * @param[in] key sets the lower bound (inclusive) * * @return const_iterator */ template template typename b_tree_base::const_iterator b_tree_base::lower_bound(const K &key) const { leaf_type *leaf = find_leaf_node(key); typename leaf_type::const_iterator leaf_it = std::lower_bound(leaf->cbegin(), leaf->cend(), key, [this](const_reference e, const K &key) { return compare(e.first, key); }); if (leaf->cend() == leaf_it) return cend(); 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 binary order (see * std::string::compare). * * @param[in] key sets the lower bound (exclusive) * * @return iterator */ template template typename b_tree_base::iterator b_tree_base::upper_bound(const K &key) { leaf_type *leaf = find_leaf_node(key); typename leaf_type::iterator leaf_it = std::upper_bound( leaf->begin(), leaf->end(), key, [this](const K &key, const_reference e) { return compare(key, e.first); }); if (leaf->end() == leaf_it) { if (leaf->get_next()) return iterator(leaf->get_next().get(), leaf->get_next()->begin()); 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 binary order (see * std::string::compare). * * @param[in] key sets the lower bound (exclusive) * * @return const_iterator */ template template typename b_tree_base::const_iterator b_tree_base::upper_bound(const K &key) const { leaf_type *leaf = find_leaf_node(key); typename leaf_type::const_iterator leaf_it = std::upper_bound(leaf->cbegin(), leaf->cend(), key, [this](const K &key, const_reference e) { return compare(key, e.first); }); if (leaf->cend() == leaf_it) { if (leaf->get_next()) return iterator(leaf->get_next().get(), leaf->get_next()->cbegin()); return cend(); } return const_iterator(leaf, leaf_it); } /** * Searches leaf with given key saving extended path including neighbors and inner node * with pointer to leaf entry (inner_ptr). */ template template typename b_tree_base::leaf_pptr b_tree_base::get_path_ext( const K &key, std::vector &path, std::vector> &neighbors, inner_pair &inner_ptr) { node_pptr temp = root; while (!temp->leaf()) { auto set = cast_inner(temp)->get_child_and_siblings(key, compare); path.push_back(std::make_pair(cast_inner(temp).get(), std::get<3>(set))); neighbors.push_back(std::make_pair(std::get<1>(set), std::get<2>(set))); if (!compare(*std::get<3>(set), key) && !compare(key, *std::get<3>(set))) { assert(inner_ptr.first == nullptr); // it should not duplicate inner_ptr = std::make_pair(cast_inner(temp), std::get<3>(set)); } temp = std::get<0>(set); } return cast_leaf(temp); } /** * Searches leaf in the right subtree of inner node (node.first), first element of which * must reside at given iterator (node.second). */ template typename b_tree_base::const_reference b_tree_base::get_suitable_entry(inner_pair &node) { node_pptr temp = node.first->get_right_child(node.second); while (!temp->leaf()) temp = cast_inner(temp)->get_left_child(cast_inner(temp)->begin()); return cast_leaf(temp)->front(); } /** * Deletes leaf from the tree leaving parent_node and neighbors in consistent state. */ template void b_tree_base::delete_leaf_ext(leaf_pptr &leaf, inner_pair &parent, bool no_left_sibling) { /* if left sibling exists then leaf is right child */ parent.first->delete_with_child(parent.second, no_left_sibling); /* correct leaf siblings pointers before deleting it */ if (leaf->get_prev()) { leaf->get_prev()->set_next(leaf->get_next()); } if (leaf->get_next()) { leaf->get_next()->set_prev(leaf->get_prev()); } deallocate(leaf); } /** * Deletes inner node from the tree leaving parent_node and neighbors in consistent state. * Also inherites remaining child. */ template void b_tree_base::delete_inner_ext( inner_pptr &node, inner_pair &parent, std::pair &neighbors, bool has_left_sibling) { if (neighbors.first) { parent.first->inherit_child(parent.second, neighbors.first, !has_left_sibling); } else if (neighbors.second) { parent.first->inherit_child(parent.second, neighbors.second, !has_left_sibling); } deallocate(node); } /** * Erases entry specified by key from the tree. */ template template typename b_tree_base::size_type b_tree_base::erase(const K &key) { using const_key = const key_type &; /* search leaf saving path, neighbors */ std::vector path; // [root, leaf) std::vector> neighbors; // (root, leaf] inner_pair to_replace; // inner node with key reference leaf_pptr leaf = get_path_ext(key, path, neighbors, to_replace); auto pop = get_pool_base(); size_type result(1); pmem::obj::transaction::run(pop, [&] { /* remove entry */ size_type deleted = leaf->erase(pop, key, compare); if (!deleted) { result = size_type(0); return; } /* still left elements in leaf -> replace pointer in inner node */ if (leaf->size() > 0) { if (to_replace.first != nullptr) { const_key new_key = get_suitable_entry(to_replace).first; to_replace.first->replace(to_replace.second, new_key); } --_size; return; } /* leaf is empty and it is root */ if (path.empty()) { --_size; return; } /* handle leaf node */ auto nbors = neighbors.back(); auto parent = path.back(); /* if left sibling exists then leaf is right child */ delete_leaf_ext(leaf, parent, nbors.first == nullptr); /* handle inner nodes */ auto node = path.back().first; if (path.size() > 1 && node->size() == 0) { path.pop_back(); neighbors.pop_back(); delete_inner_ext(node, path.back(), nbors, neighbors.back().first != nullptr); } /* replace pointer in inner node */ if (to_replace.first) { const_key new_key = get_suitable_entry(to_replace).first; to_replace.first->replace(to_replace.second, new_key); } /* one of the main subtrees deleted, other will become root */ if (path.back().first->size() == 0) { if (nbors.first) { cast_inner(root) = nbors.first; } else if (nbors.second) { cast_inner(root) = nbors.second; } } --_size; }); /* all done, return */ return result; } template typename b_tree_base::iterator b_tree_base::begin() { return iterator(leftmost_leaf()); } template typename b_tree_base::iterator b_tree_base::end() { leaf_type *leaf = rightmost_leaf(); return iterator(leaf, leaf->end()); } template typename b_tree_base::const_iterator b_tree_base::begin() const { return const_iterator(leftmost_leaf()); } template typename b_tree_base::const_iterator b_tree_base::end() const { const leaf_type *leaf = rightmost_leaf(); return const_iterator(leaf, leaf->end()); } template typename b_tree_base::const_iterator b_tree_base::cbegin() const { return begin(); } template typename b_tree_base::const_iterator b_tree_base::cend() const { return end(); } template typename b_tree_base::reverse_iterator b_tree_base::rbegin() { return reverse_iterator(end()); } template typename b_tree_base::reverse_iterator b_tree_base::rend() { return reverse_iterator(begin()); } template typename b_tree_base::size_type b_tree_base::size() const noexcept { return _size; } template typename b_tree_base::reference b_tree_base::operator[](size_type pos) { leaf_type *temp = leftmost_leaf(); while (temp != nullptr && pos >= temp->size()) { pos -= temp->size(); temp = temp->get_next().get(); } return temp->operator[](pos); } template typename b_tree_base::const_reference b_tree_base::operator[](size_type pos) const { leaf_type *temp = leftmost_leaf(); while (temp != nullptr && pos >= temp->size()) { pos -= temp->size(); temp = temp->get_next().get(); } return temp->operator[](pos); } template const typename b_tree_base::key_type & b_tree_base::get_last_key(const node_pptr &node) { if (node->leaf()) { return cast_leaf(node.get())->back().first; } else { return cast_inner(node.get())->back(); } } template void b_tree_base::create_new_root(const key_type &key, node_pptr &l_child, node_pptr &r_child) { assert(l_child != nullptr); assert(r_child != nullptr); assert(pmemobj_tx_stage() == TX_STAGE_WORK); cast_inner(root) = allocate_inner(root->level() + 1, key, l_child, r_child); } template typename b_tree_base::inner_type::const_iterator b_tree_base::split_half(pool_base &pop, inner_pptr &node, inner_pptr &other, key_pptr &partition_key) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(other == nullptr); other = allocate_inner(node->level()); return other->move(pop, *node, partition_key); } /* when root is the only inner node */ template void b_tree_base::split_inner_node(pool_base &pop, inner_pptr &src_node) { assert(root == src_node); pmem::obj::transaction::run(pop, [&] { node_pptr other(nullptr); key_pptr partition_key(nullptr); split_half(pop, src_node, cast_inner(other), partition_key); assert(partition_key != nullptr); create_new_root(*partition_key, cast_node(src_node), other); }); } /* when root is not the only inner node (2 or more inner node layers) */ template void b_tree_base::split_inner_node(pool_base &pop, inner_pptr &src_node, inner_type *parent_node) { pmem::obj::transaction::run(pop, [&] { node_pptr other(nullptr); key_pptr partition_key(nullptr); split_half(pop, src_node, cast_inner(other), partition_key); assert(partition_key != nullptr); parent_node->update_splitted_child(pop, *partition_key, cast_node(src_node), other, compare); }); } /* split leaf in case when root is leaf */ template template std::pair::iterator, bool> b_tree_base::split_leaf_node(pool_base &pop, leaf_pptr &split_leaf, K &&key, M &&obj) { assert(split_leaf->full()); leaf_pptr node; std::pair result(nullptr, false); auto middle = split_leaf->begin() + split_leaf->size() / 2; bool less = compare(std::forward(key), middle->first); // move second half into node and insert new element where needed pmem::obj::transaction::run(pop, [&] { node = allocate_leaf(); node->move(pop, split_leaf, compare); /* insert entry(key, obj) into needed half */ if (less) { result = internal_insert(split_leaf, std::forward(key), std::forward(obj)); } else { result = internal_insert(node, std::forward(key), std::forward(obj)); } create_new_root(node->front().first, cast_node(split_leaf), cast_node(node)); // re-set node's pointers node->set_next(split_leaf->get_next()); node->set_prev(split_leaf); if (split_leaf->get_next()) { split_leaf->get_next()->set_prev(node); } split_leaf->set_next(node); }); assert(!compare(result.first->first, key) && !compare(key, result.first->first)); return result; } /* split leaf in case when root is not leaf */ template template std::pair::iterator, bool> b_tree_base::split_leaf_node(pool_base &pop, inner_type *parent_node, leaf_pptr &split_leaf, K &&key, M &&obj) { assert(split_leaf->full()); leaf_pptr node; std::pair result(nullptr, false); auto middle = split_leaf->begin() + split_leaf->size() / 2; bool less = compare(std::forward(key), middle->first); // move second half into node and insert new element where needed pmem::obj::transaction::run(pop, [&] { node = allocate_leaf(); node->move(pop, split_leaf, compare); /* insert entry(key, obj) into needed half */ if (less) { result = internal_insert(split_leaf, std::forward(key), std::forward(obj)); } else { result = internal_insert(node, std::forward(key), std::forward(obj)); } // take care of parent node parent_node->update_splitted_child(pop, node->front().first, cast_node(split_leaf), cast_node(node), compare); // re-set node's pointers node->set_next(split_leaf->get_next()); node->set_prev(split_leaf); if (split_leaf->get_next()) { split_leaf->get_next()->set_prev(node); } split_leaf->set_next(node); }); assert(!compare(result.first->first, key) && !compare(key, result.first->first)); return result; } template typename b_tree_base::leaf_type * b_tree_base::find_leaf_node(const key_type &key) const { assert(root != nullptr); node_pptr node = root; while (!node->leaf()) { node = cast_inner(node)->get_child(key); } return cast_leaf(node).get(); } template template typename b_tree_base::leaf_type * b_tree_base::find_leaf_node(const K &key) const { assert(root != nullptr); node_pptr node = root; while (!node->leaf()) { node = cast_inner(node)->get_child(key, compare); } return cast_leaf(node).get(); } template template typename b_tree_base::leaf_pptr b_tree_base::find_leaf_to_insert(const K &key, path_type &path) const { assert(root != nullptr); node_pptr node = root; while (!node->leaf()) { path.push_back(cast_inner(node)); node = cast_inner(node)->get_child(key, compare); } return cast_leaf(node); } template typename b_tree_base::path_type::const_iterator b_tree_base::find_full_node(const path_type &path) { auto i = path.end() - 1; for (; i > path.begin(); --i) { if (!(*i)->full()) return i; } return i; } template template std::pair::iterator, bool> b_tree_base::internal_insert(leaf_pptr leaf, K &&key, M &&obj) { auto idxs_pos = leaf->lower_bound(std::forward(key), compare); if (idxs_pos != leaf->end() && !compare(idxs_pos->first, std::forward(key)) && !compare(std::forward(key), idxs_pos->first)) { return std::pair(iterator(leaf.get(), idxs_pos), false); } auto pop = get_pool_base(); typename leaf_type::iterator res; pmem::obj::transaction::run(pop, [&] { res = leaf->insert(idxs_pos, std::forward(key), std::forward(obj)); ++_size; }); return std::pair(iterator(leaf.get(), res), true); } template typename b_tree_base::key_compare & b_tree_base::key_comp() { return compare; } template const typename b_tree_base::key_compare & b_tree_base::key_comp() const { return compare; } template typename b_tree_base::leaf_type * b_tree_base::leftmost_leaf() const { assert(root != nullptr); node_pptr node = root; while (!node->leaf()) { inner_type *inner_node = cast_inner(node).get(); node = inner_node->get_left_child(inner_node->begin()); } return cast_leaf(node).get(); } template typename b_tree_base::leaf_type * b_tree_base::rightmost_leaf() const { assert(root != nullptr); node_pptr node = root; while (!node->leaf()) { inner_type *inner_node = cast_inner(node).get(); node = inner_node->get_left_child(inner_node->end()); } return cast_leaf(node).get(); } template typename b_tree_base::inner_pptr & b_tree_base::cast_inner(node_pptr &node) { return reinterpret_cast(node); } template typename b_tree_base::inner_type * b_tree_base::cast_inner(node_t *node) { return static_cast(node); } template typename b_tree_base::leaf_pptr & b_tree_base::cast_leaf(node_pptr &node) { return reinterpret_cast(node); } template typename b_tree_base::leaf_type * b_tree_base::cast_leaf(node_t *node) { return static_cast(node); } template typename b_tree_base::node_pptr & b_tree_base::cast_node(leaf_pptr &node) { return reinterpret_cast(node); } template typename b_tree_base::node_pptr & b_tree_base::cast_node(inner_pptr &node) { return reinterpret_cast(node); } template template inline typename b_tree_base::inner_pptr b_tree_base::allocate_inner(Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); return make_persistent(std::forward(args)...); } template template inline typename b_tree_base::leaf_pptr b_tree_base::allocate_leaf(Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); return make_persistent(std::forward(args)...); } template inline void b_tree_base::deallocate(node_pptr &node) { assert(node != nullptr); if (node->leaf()) { deallocate(cast_leaf(node)); } else { deallocate(cast_inner(node)); } } template inline void b_tree_base::deallocate(leaf_pptr &node) { assert(node != nullptr); pool_base pop = get_pool_base(); pmem::obj::transaction::run(pop, [&] { delete_persistent(node); node = nullptr; }); } template inline void b_tree_base::deallocate(inner_pptr &node) { assert(node != nullptr); pool_base pop = get_pool_base(); pmem::obj::transaction::run(pop, [&] { delete_persistent(node); node = nullptr; }); } template PMEMobjpool *b_tree_base::get_objpool() { PMEMoid oid = pmemobj_oid(this); return pmemobj_pool_by_oid(oid); } template pool_base b_tree_base::get_pool_base() { return pool_base(get_objpool()); } } /* namespace internal */ template , std::size_t degree = 64> class b_tree : public internal::b_tree_base { private: using base_type = internal::b_tree_base; public: using base_type::begin; using base_type::end; using base_type::erase; using base_type::find; using base_type::try_emplace; /* type definitions */ using key_type = typename base_type::key_type; using mapped_type = typename base_type::mapped_type; using value_type = typename base_type::value_type; using iterator = typename base_type::iterator; using const_iterator = typename base_type::const_iterator; using reverse_iterator = typename base_type::reverse_iterator; explicit b_tree() : base_type() { } ~b_tree() { } b_tree(const b_tree &) = delete; b_tree &operator=(const b_tree &) = delete; }; } // namespace kv } // namespace persistent #endif // PERSISTENT_B_TREE pmemkv-1.5.0/src/engines-experimental/tree3.cc000066400000000000000000000450241410000423300212470ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #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, "pmemkv_tree3") { 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); } static factory_registerer register_tree3(std::unique_ptr(new tree3_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-experimental/tree3.h000066400000000000000000000147021410000423300211100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_TREE3_H #define LIBPMEMKV_TREE3_H #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(const tree3 &) = delete; tree3 &operator=(const tree3 &) = delete; ~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: vector> leaves_prealloc; // persisted but unused leaves unique_ptr tree_top; // pointer to uppermost inner node }; class tree3_factory : public engine_base::factory_base { public: unique_ptr create(unique_ptr cfg) override { check_config_null(get_name(), cfg); return unique_ptr(new tree3(move(cfg))); }; std::string get_name() override { return "tree3"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_TREE3_H */ pmemkv-1.5.0/src/engines-testing/000077500000000000000000000000001410000423300166715ustar00rootroot00000000000000pmemkv-1.5.0/src/engines-testing/dram_vcmap.cc000066400000000000000000000005601410000423300213120ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "dram_vcmap.h" namespace pmem { namespace kv { template<> std::string dram_vcmap::name() { return "dram_vcmap"; } static factory_registerer register_dram_vcmap( std::unique_ptr(new dram_vcmap_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines-testing/dram_vcmap.h000066400000000000000000000016071410000423300211570ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #pragma once #include "../engines/basic_vcmap.h" namespace pmem { namespace kv { namespace internal { class std_allocator_factory { public: template using allocator_type = std::allocator; template static allocator_type create(internal::config& cfg) { return allocator_type(); } }; } /* namespace internal */ using dram_vcmap = basic_vcmap; class dram_vcmap_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); return std::unique_ptr(new dram_vcmap(std::move(cfg))); }; std::string get_name() override { return "dram_vcmap"; }; }; } /* namespace kv */ } /* namespace pmem */ pmemkv-1.5.0/src/engines/000077500000000000000000000000001410000423300152165ustar00rootroot00000000000000pmemkv-1.5.0/src/engines/basic_vcmap.h000066400000000000000000000202711410000423300176400ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_BASIC_VCMAP_H #define LIBPMEMKV_BASIC_VCMAP_H #include "../engine.h" #include "../out.h" #include #include #include #include #include namespace pmem { namespace kv { template class basic_vcmap : public engine_base { class basic_vcmap_iterator; class basic_vcmap_const_iterator; public: basic_vcmap(std::unique_ptr cfg); ~basic_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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; private: using ch_allocator_t = typename AllocatorFactory::template allocator_type; using pmem_string = std::basic_string, ch_allocator_t>; using kv_allocator_t = typename AllocatorFactory::template allocator_type< std::pair>; 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; }; template basic_vcmap::basic_vcmap(std::unique_ptr cfg) : kv_allocator(AllocatorFactory::template create(*cfg)), ch_allocator(kv_allocator), pmem_kv_container(std::scoped_allocator_adaptor(kv_allocator)) { LOG("Started ok"); } template basic_vcmap::~basic_vcmap() { LOG("Stopped ok"); } template std::string basic_vcmap::name() { return "basic_vcmap"; } template status basic_vcmap::count_all(std::size_t &cnt) { LOG("count_all"); cnt = pmem_kv_container.size(); return status::OK; } template status basic_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; } template status basic_vcmap::exists(string_view key) { LOG("exists for key=" << std::string(key.data(), key.size())); typename 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); } template status basic_vcmap::get(string_view key, get_v_callback *callback, void *arg) { LOG("get key=" << std::string(key.data(), key.size())); typename 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; } template status basic_vcmap::put(string_view key, string_view value) { LOG("put key=" << std::string(key.data(), key.size()) << ", value.size=" << std::to_string(value.size())); typename map_t::value_type kv_pair( std::piecewise_construct, std::forward_as_tuple(key.data(), key.size(), ch_allocator), std::forward_as_tuple(ch_allocator)); typename map_t::accessor acc; pmem_kv_container.insert(acc, std::move(kv_pair)); acc->second.assign(value.data(), value.size()); return status::OK; } template status basic_vcmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string bool erased = pmem_kv_container.erase( pmem_string(key.data(), key.size(), ch_allocator)); return (erased ? status::OK : status::NOT_FOUND); } template class basic_vcmap::basic_vcmap_const_iterator : virtual public internal::iterator_base { using container_type = basic_vcmap::map_t; using ch_allocator_t = basic_vcmap::ch_allocator_t; public: basic_vcmap_const_iterator(container_type *container, ch_allocator_t *ca); status seek(string_view key) final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; typename container_type::accessor acc_; ch_allocator_t *ch_allocator; }; template class basic_vcmap::basic_vcmap_iterator : public basic_vcmap::basic_vcmap_const_iterator { using container_type = basic_vcmap::map_t; using ch_allocator_t = basic_vcmap::ch_allocator_t; public: basic_vcmap_iterator(container_type *container, ch_allocator_t *ca); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; }; template internal::iterator_base *basic_vcmap::new_iterator() { return new basic_vcmap_iterator{&pmem_kv_container, &ch_allocator}; } template internal::iterator_base *basic_vcmap::new_const_iterator() { return new basic_vcmap_const_iterator{&pmem_kv_container, &ch_allocator}; } template basic_vcmap::basic_vcmap_const_iterator::basic_vcmap_const_iterator( container_type *c, ch_allocator_t *ca) : container(c), ch_allocator(ca) { } template basic_vcmap::basic_vcmap_iterator::basic_vcmap_iterator( container_type *c, ch_allocator_t *ca) : basic_vcmap::basic_vcmap_const_iterator(c, ca) { } template status basic_vcmap::basic_vcmap_const_iterator::seek(string_view key) { init_seek(); if (container->find(acc_, pmem_string(key.data(), key.size(), *ch_allocator))) return status::OK; return status::NOT_FOUND; } template result basic_vcmap::basic_vcmap_const_iterator::key() { assert(!acc_.empty()); return string_view(acc_->first.data(), acc_->first.length()); } template result> basic_vcmap::basic_vcmap_const_iterator::read_range(size_t pos, size_t n) { assert(!acc_.empty()); if (pos + n > acc_->second.size() || pos + n < pos) n = acc_->second.size() - pos; return {{acc_->second.c_str() + pos, acc_->second.c_str() + pos + n}}; } template result> basic_vcmap::basic_vcmap_iterator::write_range(size_t pos, size_t n) { assert(!this->acc_.empty()); if (pos + n > this->acc_->second.size() || pos + n < pos) n = this->acc_->second.size() - pos; log.push_back({std::string(this->acc_->second.c_str() + pos, n), pos}); auto &val = log.back().first; return {{&val[0], &val[n]}}; } template status basic_vcmap::basic_vcmap_iterator::commit() { for (auto &p : log) { auto dest = &(this->acc_->second[0]) + p.second; std::copy(p.first.begin(), p.first.end(), dest); } log.clear(); return status::OK; } template void basic_vcmap::basic_vcmap_iterator::abort() { log.clear(); } } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_BASIC_VCMAP_H */ pmemkv-1.5.0/src/engines/blackhole.cc000066400000000000000000000077041410000423300174610ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "blackhole.h" #include <../out.h> 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; } internal::iterator_base *blackhole::new_iterator() { LOG("create write iterator"); return new blackhole_iterator{}; } internal::iterator_base *blackhole::new_const_iterator() { LOG("create read iterator"); return new blackhole_iterator{}; } /* required for logging */ std::string blackhole::blackhole_iterator::name() { return "blackhole iterator"; } status blackhole::blackhole_iterator::seek(string_view key) { LOG("seek to key=" << std::string(key.data(), key.size())); return status::OK; } result blackhole::blackhole_iterator::key() { LOG("key"); return status::NOT_FOUND; } result> blackhole::blackhole_iterator::read_range(size_t pos, size_t n) { LOG("read_range, pos=" << pos << " n=" << n); return status::NOT_FOUND; } static factory_registerer register_blackhole( std::unique_ptr(new blackhole_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines/blackhole.h000066400000000000000000000043041410000423300173140ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_BLACKHOLE_H #define LIBPMEMKV_BLACKHOLE_H #include "../engine.h" namespace pmem { namespace kv { class blackhole : public engine_base { class blackhole_iterator; 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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; }; class blackhole::blackhole_iterator : public internal::iterator_base { public: status seek(string_view key) final; result key() final; result> read_range(size_t pos, size_t n) final; private: std::string name(); }; class blackhole_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { return std::unique_ptr(new blackhole(std::move(cfg))); }; std::string get_name() override { return "blackhole"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_BLACKHOLE_H */ pmemkv-1.5.0/src/engines/cmap.cc000066400000000000000000000112711410000423300164470ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "cmap.h" #include "../out.h" #include namespace pmem { namespace kv { cmap::cmap(std::unique_ptr cfg) : pmemobj_engine_base(cfg, "pmemkv") { 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(); auto it = container->begin(); auto end = container->end(); return internal::iterate_through_pairs(it, end, callback, arg); } 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(); }); } } internal::iterator_base *cmap::new_iterator() { return new cmap_iterator{container}; } internal::iterator_base *cmap::new_const_iterator() { return new cmap_iterator{container}; } cmap::cmap_iterator::cmap_iterator(container_type *c) : container(c), pop(pmem::obj::pool_by_vptr(c)) { } cmap::cmap_iterator::cmap_iterator(container_type *c) : cmap::cmap_iterator(c) { } status cmap::cmap_iterator::seek(string_view key) { init_seek(); if (container->find(acc_, key)) return status::OK; return status::NOT_FOUND; } result cmap::cmap_iterator::key() { assert(!acc_.empty()); return string_view(acc_->first.c_str(), acc_->first.length()); } result> cmap::cmap_iterator::read_range(size_t pos, size_t n) { assert(!acc_.empty()); if (pos + n > acc_->second.size() || pos + n < pos) n = acc_->second.size() - pos; return {{acc_->second.c_str() + pos, acc_->second.c_str() + pos + n}}; } result> cmap::cmap_iterator::write_range(size_t pos, size_t n) { assert(!acc_.empty()); if (pos + n > acc_->second.size() || pos + n < pos) n = acc_->second.size() - pos; log.push_back({std::string(acc_->second.c_str() + pos, n), pos}); auto &val = log.back().first; return {{&val[0], &val[n]}}; } status cmap::cmap_iterator::commit() { pmem::obj::transaction::run(pop, [&] { for (auto &p : log) { auto dest = acc_->second.range(p.second, p.first.size()); std::copy(p.first.begin(), p.first.end(), dest.begin()); } }); log.clear(); return status::OK; } void cmap::cmap_iterator::abort() { log.clear(); } static factory_registerer register_cmap(std::unique_ptr(new cmap_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines/cmap.h000066400000000000000000000064361410000423300163200ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_CMAP_H #define LIBPMEMKV_CMAP_H #include "../iterator.h" #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 { template class cmap_iterator; public: cmap(std::unique_ptr cfg); ~cmap(); cmap() = delete; 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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() final; private: void Recover(); internal::cmap::map_t *container; }; template <> class cmap::cmap_iterator : public internal::iterator_base { using container_type = internal::cmap::map_t; public: cmap_iterator(container_type *container); status seek(string_view key) final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; container_type::accessor acc_; pmem::obj::pool_base pop; }; template <> class cmap::cmap_iterator : public cmap::cmap_iterator { using container_type = internal::cmap::map_t; public: cmap_iterator(container_type *container); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; }; class cmap_factory : public engine_base::factory_base { public: std::unique_ptr create(std::unique_ptr cfg) override { check_config_null(get_name(), cfg); return std::unique_ptr(new cmap(std::move(cfg))); }; std::string get_name() override { return "cmap"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_CMAP_H */ pmemkv-1.5.0/src/engines/vcmap.cc000066400000000000000000000005311410000423300166320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "vcmap.h" namespace pmem { namespace kv { template <> std::string vcmap::name() { return "vcmap"; } static factory_registerer register_vcmap(std::unique_ptr(new vcmap_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines/vcmap.h000066400000000000000000000021051410000423300164730ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_VCMAP_H #define LIBPMEMKV_VCMAP_H #include "basic_vcmap.h" #include "pmem_allocator.h" #ifdef USE_LIBMEMKIND_NAMESPACE namespace memkind_ns = libmemkind::pmem; #else namespace memkind_ns = pmem; #endif namespace pmem { namespace kv { namespace internal { class memkind_allocator_factory { public: template using allocator_type = memkind_ns::allocator; template static allocator_type create(internal::config &cfg) { return allocator_type(cfg.get_path(), cfg.get_size()); } }; } using vcmap = basic_vcmap; class vcmap_factory : public engine_base::factory_base { public: virtual std::unique_ptr create(std::unique_ptr cfg) { check_config_null(get_name(), cfg); return std::unique_ptr(new vcmap(std::move(cfg))); }; virtual std::string get_name() { return "vcmap"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_VCMAP_H */ pmemkv-1.5.0/src/engines/vsmap.cc000066400000000000000000000247271410000423300166670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "vsmap.h" #include "../comparator/comparator.h" #include "../comparator/volatile_comparator.h" #include "../out.h" #include #include namespace pmem { namespace kv { vsmap::vsmap(std::unique_ptr cfg) : kv_allocator(cfg->get_path(), cfg->get_size()), pmem_kv_container(internal::volatile_compare(internal::extract_comparator(*cfg)), kv_allocator), config(std::move(cfg)) { 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())); // 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(); cnt = internal::distance(it, end); 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())); // 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(); cnt = internal::distance(it, end); 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())); 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)); cnt = internal::distance(it, end); 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())); 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)); cnt = internal::distance(it, end); 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 (pmem_kv_container.key_comp()(key1, key2)) { // 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)); result = internal::distance(it, end); } cnt = result; return status::OK; } status vsmap::get_all(get_kv_callback *callback, void *arg) { LOG("get_all"); return internal::iterate_through_pairs(pmem_kv_container.begin(), pmem_kv_container.end(), callback, arg); } 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(); return internal::iterate_through_pairs(it, end, callback, arg); } 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(); return internal::iterate_through_pairs(it, end, callback, arg); } 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)); return internal::iterate_through_pairs(it, end, callback, arg); } 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)); return internal::iterate_through_pairs(it, end, callback, arg); } 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 (pmem_kv_container.key_comp()(key1, key2)) { // 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)); return internal::iterate_through_pairs(it, end, callback, arg); } 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 - starting from C++17 std::map has try_emplace method which could be more // efficient auto res = pmem_kv_container.emplace( std::piecewise_construct, std::forward_as_tuple(key.data(), key.size()), std::forward_as_tuple(value.data(), value.size())); if (!res.second) { auto it = res.first; it->second.assign(value.data(), value.size()); } return status::OK; } status vsmap::remove(string_view key) { LOG("remove key=" << std::string(key.data(), key.size())); // XXX - do not create temporary string bool erased = pmem_kv_container.erase(key_type(key.data(), key.size(), kv_allocator)); return (erased ? status::OK : status::NOT_FOUND); } internal::iterator_base *vsmap::new_iterator() { return new vsmap_iterator{&pmem_kv_container, &kv_allocator}; } internal::iterator_base *vsmap::new_const_iterator() { return new vsmap_iterator{&pmem_kv_container, &kv_allocator}; } vsmap::vsmap_iterator::vsmap_iterator(container_type *c, vsmap::map_allocator_type *alloc) : container(c), kv_allocator(alloc) { } vsmap::vsmap_iterator::vsmap_iterator(container_type *c, vsmap::map_allocator_type *alloc) : vsmap::vsmap_iterator(c, alloc) { } status vsmap::vsmap_iterator::seek(string_view key) { init_seek(); it_ = container->find(vsmap::key_type(key.data(), key.size(), *kv_allocator)); if (it_ != container->end()) return status::OK; return status::NOT_FOUND; } status vsmap::vsmap_iterator::seek_lower(string_view key) { init_seek(); it_ = container->lower_bound( vsmap::key_type(key.data(), key.size(), *kv_allocator)); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status vsmap::vsmap_iterator::seek_lower_eq(string_view key) { init_seek(); it_ = container->upper_bound( vsmap::key_type(key.data(), key.size(), *kv_allocator)); if (it_ == container->begin()) { it_ = container->end(); return status::NOT_FOUND; } --it_; return status::OK; } status vsmap::vsmap_iterator::seek_higher(string_view key) { init_seek(); it_ = container->upper_bound( vsmap::key_type(key.data(), key.size(), *kv_allocator)); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status vsmap::vsmap_iterator::seek_higher_eq(string_view key) { init_seek(); it_ = container->lower_bound( vsmap::key_type(key.data(), key.size(), *kv_allocator)); if (it_ == container->end()) return status::NOT_FOUND; return status::OK; } status vsmap::vsmap_iterator::seek_to_first() { init_seek(); if (container->empty()) return status::NOT_FOUND; it_ = container->begin(); return status::OK; } status vsmap::vsmap_iterator::seek_to_last() { init_seek(); if (container->empty()) return status::NOT_FOUND; it_ = container->end(); --it_; return status::OK; } status vsmap::vsmap_iterator::is_next() { auto tmp = it_; if (tmp == container->end() || ++tmp == container->end()) return status::NOT_FOUND; return status::OK; } status vsmap::vsmap_iterator::next() { init_seek(); if (it_ == container->end() || ++it_ == container->end()) return status::NOT_FOUND; return status::OK; } status vsmap::vsmap_iterator::prev() { init_seek(); if (it_ == container->begin()) return status::NOT_FOUND; --it_; return status::OK; } result vsmap::vsmap_iterator::key() { assert(it_ != container->end()); return string_view(it_->first.data(), it_->first.length()); } result> vsmap::vsmap_iterator::read_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.size() || pos + n < pos) n = it_->second.size() - pos; return {{it_->second.data() + pos, it_->second.data() + pos + n}}; } result> vsmap::vsmap_iterator::write_range(size_t pos, size_t n) { assert(it_ != container->end()); if (pos + n > it_->second.size() || pos + n < pos) n = it_->second.size() - pos; log.push_back({std::string(&(it_->second[pos]), n), pos}); auto &val = log.back().first; return {{&val[0], &val[0] + n}}; } status vsmap::vsmap_iterator::commit() { for (auto &p : log) { auto dest = &(it_->second[0]) + p.second; std::copy(p.first.begin(), p.first.end(), dest); } log.clear(); return status::OK; } void vsmap::vsmap_iterator::abort() { log.clear(); } static factory_registerer register_vsmap(std::unique_ptr(new vsmap_factory)); } // namespace kv } // namespace pmem pmemkv-1.5.0/src/engines/vsmap.h000066400000000000000000000076711410000423300165300ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_VSMAP_H #define LIBPMEMKV_VSMAP_H #include "../comparator/volatile_comparator.h" #include "../engine.h" #include "../iterator.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 { template class vsmap_iterator; 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; internal::iterator_base *new_iterator() final; internal::iterator_base *new_const_iterator() 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>; map_allocator_type kv_allocator; map_type pmem_kv_container; std::unique_ptr config; }; template <> class vsmap::vsmap_iterator : public internal::iterator_base { using container_type = vsmap::map_type; public: vsmap_iterator(container_type *container, vsmap::map_allocator_type *kv_allocator); status seek(string_view key) final; status seek_lower(string_view key) final; status seek_lower_eq(string_view key) final; status seek_higher(string_view key) final; status seek_higher_eq(string_view key) final; status seek_to_first() final; status seek_to_last() final; status is_next() final; status next() final; status prev() final; result key() final; result> read_range(size_t pos, size_t n) final; protected: container_type *container; vsmap::map_allocator_type *kv_allocator; container_type::iterator it_; }; template <> class vsmap::vsmap_iterator : public vsmap::vsmap_iterator { using container_type = vsmap::map_type; public: vsmap_iterator(container_type *container, vsmap::map_allocator_type *kv_allocator); result> write_range(size_t pos, size_t n) final; status commit() final; void abort() final; private: std::vector> log; }; class vsmap_factory : public engine_base::factory_base { public: virtual std::unique_ptr create(std::unique_ptr cfg) { check_config_null(get_name(), cfg); return std::unique_ptr(new vsmap(std::move(cfg))); }; virtual std::string get_name() { return "vsmap"; }; }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_VSMAP_H */ pmemkv-1.5.0/src/exceptions.h000066400000000000000000000025521410000423300161240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2020, Intel Corporation */ #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) { } }; struct comparator_mismatch : error { comparator_mismatch(const std::string &msg) : error(msg, PMEMKV_STATUS_COMPARATOR_MISMATCH) { } }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_EXCEPTIONS_H */ pmemkv-1.5.0/src/fast_hash.cc000066400000000000000000000036551410000423300160460ustar00rootroot00000000000000/* * The fast-hash algorithm is covered by the MIT License: * * Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com) * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "fast_hash.h" #include /* * mix -- (internal) helper for the fast-hash mixing step */ static inline uint64_t mix(uint64_t h) { h ^= h >> 23; h *= 0x2127599bf4325c37ULL; return h ^ h >> 47; } /* * hash -- calculate the hash of a piece of memory */ uint64_t fast_hash(size_t key_size, const char *key) { /* fast-hash, by Zilong Tan */ const uint64_t m = 0x880355f21e6d1965ULL; const uint64_t *pos = (const uint64_t *)key; const uint64_t *end = pos + (key_size / 8); uint64_t h = key_size * m; while (pos != end) h = (h ^ mix(*pos++)) * m; if (key_size & 7) { uint64_t shift = (key_size & 7) * 8; uint64_t mask = (1ULL << shift) - 1; uint64_t v = htole64(*pos) & mask; h = (h ^ mix(v)) * m; } return mix(h); } pmemkv-1.5.0/src/fast_hash.h000066400000000000000000000025271410000423300157050ustar00rootroot00000000000000/* * The fast-hash algorithm is covered by the MIT License: * * Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com) * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef FAST_HASH_H #define FAST_HASH_H #include #include uint64_t fast_hash(size_t key_size, const char *key); #endif /* FAST_HASH_H */ pmemkv-1.5.0/src/iterator.cc000066400000000000000000000023121410000423300157240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterator.h" namespace pmem { namespace kv { namespace internal { status iterator_base::seek_lower(string_view key) { return status::NOT_SUPPORTED; } status iterator_base::seek_lower_eq(string_view key) { return status::NOT_SUPPORTED; } status iterator_base::seek_higher(string_view key) { return status::NOT_SUPPORTED; } status iterator_base::seek_higher_eq(string_view key) { return status::NOT_SUPPORTED; } status iterator_base::seek_to_first() { return status::NOT_SUPPORTED; } status iterator_base::seek_to_last() { return status::NOT_SUPPORTED; } status iterator_base::is_next() { return status::NOT_SUPPORTED; } status iterator_base::next() { return status::NOT_SUPPORTED; } status iterator_base::prev() { return status::NOT_SUPPORTED; } result> iterator_base::write_range(size_t pos, size_t n) { return {status::NOT_SUPPORTED}; } status iterator_base::commit() { return status::NOT_SUPPORTED; } void iterator_base::abort() { /* by default NOT_SUPPORTED */ } void iterator_base::init_seek() { abort(); } } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ pmemkv-1.5.0/src/iterator.h000066400000000000000000000034071410000423300155740ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #ifndef LIBPMEMKV_ITERATOR_H #define LIBPMEMKV_ITERATOR_H #include "libpmemkv.hpp" #include #include #include namespace pmem { namespace kv { namespace internal { class iterator_base { public: virtual ~iterator_base() = default; virtual status seek(string_view key) = 0; virtual status seek_lower(string_view key); virtual status seek_lower_eq(string_view key); virtual status seek_higher(string_view key); virtual status seek_higher_eq(string_view key); virtual status seek_to_first(); virtual status seek_to_last(); virtual status is_next(); virtual status next(); virtual status prev(); virtual result key() = 0; virtual result> read_range(size_t pos, size_t n) = 0; virtual result> write_range(size_t pos, size_t n); virtual status commit(); virtual void abort(); protected: virtual void init_seek(); }; template std::size_t distance(It first, It last) { auto dist = std::distance(first, last); assert(dist >= 0); return static_cast(dist); } /** * Helper function to iterate between specified range and execute * callback on every item. */ template status iterate_through_pairs(It first, It last, get_kv_callback *callback, void *arg) { for (auto it = first; it != last; ++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; } } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_ITERATOR_H */ pmemkv-1.5.0/src/libpmemkv.cc000066400000000000000000000532421410000423300160710ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include #include #include "comparator/comparator.h" #include "config.h" #include "engine.h" #include "exceptions.h" #include "iterator.h" #include "libpmemkv.h" #include "libpmemkv.hpp" #include "libpmemobj++/pexceptions.hpp" #include "out.h" #include "transaction.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 pmemkv_comparator * comparator_from_internal(pmem::kv::internal::comparator *comparator) { return reinterpret_cast(comparator); } static inline pmem::kv::internal::comparator * comparator_to_internal(pmemkv_comparator *comparator) { return reinterpret_cast(comparator); } 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); } static inline pmemkv_tx *tx_from_internal(pmem::kv::internal::transaction *tx) { return reinterpret_cast(tx); } static inline pmem::kv::internal::transaction *tx_to_internal(pmemkv_tx *tx) { return reinterpret_cast(tx); } pmem::kv::internal::iterator_base *iterator_to_base(pmemkv_iterator *it) { return reinterpret_cast(it); } static inline pmemkv_iterator * iterator_from_internal(pmem::kv::internal::iterator_base *it) { return reinterpret_cast(it); } template static inline int catch_and_return_status(const char *func_name, Function &&f) { int status = PMEMKV_STATUS_UNKNOWN_ERROR; try { status = static_cast(f()); } catch (pmem::kv::internal::error &e) { out_err_stream(func_name) << e.what(); status = e.status_code; } catch (std::bad_alloc &e) { out_err_stream(func_name) << e.what(); status = PMEMKV_STATUS_OUT_OF_MEMORY; } catch (std::runtime_error &e) { out_err_stream(func_name) << e.what(); status = PMEMKV_STATUS_UNKNOWN_ERROR; } catch (std::invalid_argument &e) { out_err_stream(func_name) << e.what(); status = PMEMKV_STATUS_INVALID_ARGUMENT; } catch (pmem::transaction_scope_error &e) { out_err_stream(func_name) << e.what(); status = PMEMKV_STATUS_TRANSACTION_SCOPE_ERROR; } catch (std::exception &e) { out_err_stream(func_name) << e.what(); status = PMEMKV_STATUS_UNKNOWN_ERROR; } catch (...) { out_err_stream(func_name) << "Unspecified error"; status = PMEMKV_STATUS_UNKNOWN_ERROR; } set_last_status(status); return status; } 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_object_cb(pmemkv_config *config, const char *key, void *value, void *(*getter)(void *), void (*deleter)(void *)) { if (!config || !getter) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { config_to_internal(config)->put_object(key, value, deleter, getter); 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_put_size(pmemkv_config *config, uint64_t value) { return pmemkv_config_put_uint64(config, "size", value); } int pmemkv_config_put_path(pmemkv_config *config, const char *value) { return pmemkv_config_put_string(config, "path", value); } /* deprecated */ int pmemkv_config_put_force_create(pmemkv_config *config, bool value) { return pmemkv_config_put_create_or_error_if_exists(config, value); } int pmemkv_config_put_create_or_error_if_exists(pmemkv_config *config, bool value) { return pmemkv_config_put_uint64(config, "create_or_error_if_exists", static_cast(value)); } int pmemkv_config_put_create_if_missing(pmemkv_config *config, bool value) { return pmemkv_config_put_uint64(config, "create_if_missing", static_cast(value)); } int pmemkv_config_put_comparator(pmemkv_config *config, pmemkv_comparator *comparator) { return pmemkv_config_put_object(config, "comparator", comparator, (void (*)(void *)) & pmemkv_comparator_delete); } int pmemkv_config_put_oid(pmemkv_config *config, PMEMoid *oid) { return pmemkv_config_put_object(config, "oid", oid, NULL); } 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; }); } pmemkv_comparator *pmemkv_comparator_new(pmemkv_compare_function *fn, const char *name, void *arg) { if (!fn || !name) { ERR() << "comparison function and name must not be NULL"; return nullptr; } try { return comparator_from_internal( new pmem::kv::internal::comparator(fn, name, arg)); } catch (const std::exception &exc) { ERR() << exc.what(); return nullptr; } catch (...) { ERR() << "Unspecified failure"; return nullptr; } } void pmemkv_comparator_delete(pmemkv_comparator *comparator) { try { delete comparator_to_internal(comparator); } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx) { if (!tx || !db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { *tx = tx_from_internal(db_to_internal(db)->begin_tx()); return PMEMKV_STATUS_OK; }); } int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb) { if (!tx) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return tx_to_internal(tx)->put(pmem::kv::string_view(k, kb), pmem::kv::string_view(v, vb)); }); } int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb) { if (!tx) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return tx_to_internal(tx)->remove(pmem::kv::string_view(k, kb)); }); } int pmemkv_tx_commit(pmemkv_tx *tx) { if (!tx) return PMEMKV_STATUS_INVALID_ARGUMENT; auto internal_tx = tx_to_internal(tx); return catch_and_return_status(__func__, [&] { return internal_tx->commit(); }); } void pmemkv_tx_abort(pmemkv_tx *tx) { if (!tx) return; auto internal_tx = tx_to_internal(tx); try { internal_tx->abort(); } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } void pmemkv_tx_end(pmemkv_tx *tx) { auto internal_tx = tx_to_internal(tx); try { delete internal_tx; } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } int pmemkv_open(const char *engine_c_str, pmemkv_config *config, pmemkv_db **db) { std::unique_ptr cfg(config_to_internal(config)); if (!db) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { auto engine = pmem::kv::storage_engine_factory::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); }); } int pmemkv_iterator_new(pmemkv_db *db, pmemkv_iterator **it) { if (!db || !it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { *it = iterator_from_internal(db_to_internal(db)->new_const_iterator()); return PMEMKV_STATUS_OK; }); } int pmemkv_write_iterator_new(pmemkv_db *db, pmemkv_write_iterator **it) { if (!db || !it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { /* pmemkv_write_iterator is returned by pointer, not by copy to be * consistent with pmemkv_iterator */ auto unique_it = std::unique_ptr( new pmemkv_write_iterator()); unique_it->iter = iterator_from_internal(db_to_internal(db)->new_iterator()); *it = unique_it.release(); return PMEMKV_STATUS_OK; }); } void pmemkv_iterator_delete(pmemkv_iterator *it) { if (!it) return; try { delete iterator_to_base(it); } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } void pmemkv_write_iterator_delete(pmemkv_write_iterator *it) { if (!it) return; try { delete iterator_to_base(it->iter); delete it; } catch (const std::exception &exc) { ERR() << exc.what(); } catch (...) { ERR() << "Unspecified failure"; } } int pmemkv_iterator_seek(pmemkv_iterator *it, const char *k, size_t kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->seek(pmem::kv::string_view(k, kb)); }); } int pmemkv_iterator_seek_lower(pmemkv_iterator *it, const char *k, size_t kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->seek_lower(pmem::kv::string_view(k, kb)); }); } int pmemkv_iterator_seek_lower_eq(pmemkv_iterator *it, const char *k, size_t kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->seek_lower_eq(pmem::kv::string_view(k, kb)); }); } int pmemkv_iterator_seek_higher(pmemkv_iterator *it, const char *k, size_t kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->seek_higher(pmem::kv::string_view(k, kb)); }); } int pmemkv_iterator_seek_higher_eq(pmemkv_iterator *it, const char *k, size_t kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->seek_higher_eq(pmem::kv::string_view(k, kb)); }); } int pmemkv_iterator_seek_to_first(pmemkv_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status( __func__, [&] { return iterator_to_base(it)->seek_to_first(); }); } int pmemkv_iterator_seek_to_last(pmemkv_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status( __func__, [&] { return iterator_to_base(it)->seek_to_last(); }); } int pmemkv_iterator_is_next(pmemkv_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->is_next(); }); } int pmemkv_iterator_next(pmemkv_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->next(); }); } int pmemkv_iterator_prev(pmemkv_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { return iterator_to_base(it)->prev(); }); } int pmemkv_iterator_key(pmemkv_iterator *it, const char **k, size_t *kb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { auto ret = iterator_to_base(it)->key(); if (!ret.is_ok()) return static_cast(ret.get_status()); auto key = std::move(ret).get_value(); *k = key.begin(); *kb = key.size(); return PMEMKV_STATUS_OK; }); } int pmemkv_iterator_read_range(pmemkv_iterator *it, size_t pos, size_t n, const char **data, size_t *rb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { auto ret = iterator_to_base(it)->read_range(pos, n); if (!ret.is_ok()) return static_cast(ret.get_status()); auto range = std::move(ret).get_value(); *data = range.begin(); *rb = range.size(); return PMEMKV_STATUS_OK; }); } int pmemkv_write_iterator_write_range(pmemkv_write_iterator *it, size_t pos, size_t n, char **data, size_t *wb) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status(__func__, [&] { auto ret = iterator_to_base(it->iter)->write_range(pos, n); if (!ret.is_ok()) return static_cast(ret.get_status()); auto range = std::move(ret).get_value(); *data = range.begin(); *wb = range.size(); return PMEMKV_STATUS_OK; }); } int pmemkv_write_iterator_commit(pmemkv_write_iterator *it) { if (!it) return PMEMKV_STATUS_INVALID_ARGUMENT; return catch_and_return_status( __func__, [&] { return iterator_to_base(it->iter)->commit(); }); } void pmemkv_write_iterator_abort(pmemkv_write_iterator *it) { if (!it) return; iterator_to_base(it->iter)->abort(); } const char *pmemkv_errormsg(void) { return out_get_errormsg(); } } /* extern "C" */ pmemkv-1.5.0/src/libpmemkv.h000066400000000000000000000155171410000423300157360ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_H #define LIBPMEMKV_H #include #include #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 #define PMEMKV_STATUS_COMPARATOR_MISMATCH 12 typedef struct pmemkv_db pmemkv_db; typedef struct pmemkv_config pmemkv_config; typedef struct pmemkv_comparator pmemkv_comparator; typedef struct pmemkv_tx pmemkv_tx; typedef struct pmemkv_iterator pmemkv_iterator; typedef struct { pmemkv_iterator *iter; } pmemkv_write_iterator; 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); typedef int pmemkv_compare_function(const char *key1, size_t keybytes1, const char *key2, size_t keybytes2, void *arg); pmemkv_comparator *pmemkv_comparator_new(pmemkv_compare_function *fn, const char *name, void *arg); void pmemkv_comparator_delete(pmemkv_comparator *comparator); 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_object_cb(pmemkv_config *config, const char *key, void *value, void *(*getter)(void *), 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_config_put_size(pmemkv_config *config, uint64_t value); int pmemkv_config_put_path(pmemkv_config *config, const char *value); int __attribute__((deprecated("use pmemkv_config_put_create_or_error_if_exists instead"))) pmemkv_config_put_force_create(pmemkv_config *config, bool value); int pmemkv_config_put_create_or_error_if_exists(pmemkv_config *config, bool value); int pmemkv_config_put_create_if_missing(pmemkv_config *config, bool value); int pmemkv_config_put_comparator(pmemkv_config *config, pmemkv_comparator *comparator); int pmemkv_config_put_oid(pmemkv_config *config, PMEMoid *oid); 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); /* This API is EXPERIMENTAL and might change. */ int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx); int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb); int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb); int pmemkv_tx_commit(pmemkv_tx *tx); void pmemkv_tx_abort(pmemkv_tx *tx); void pmemkv_tx_end(pmemkv_tx *tx); /* This API is EXPERIMENTAL and might change. */ int pmemkv_iterator_new(pmemkv_db *db, pmemkv_iterator **it); int pmemkv_write_iterator_new(pmemkv_db *db, pmemkv_write_iterator **it); void pmemkv_iterator_delete(pmemkv_iterator *it); void pmemkv_write_iterator_delete(pmemkv_write_iterator *it); int pmemkv_iterator_seek(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_lower(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_lower_eq(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_higher(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_higher_eq(pmemkv_iterator *it, const char *k, size_t kb); int pmemkv_iterator_seek_to_first(pmemkv_iterator *it); int pmemkv_iterator_seek_to_last(pmemkv_iterator *it); int pmemkv_iterator_is_next(pmemkv_iterator *it); int pmemkv_iterator_next(pmemkv_iterator *it); int pmemkv_iterator_prev(pmemkv_iterator *it); int pmemkv_iterator_key(pmemkv_iterator *it, const char **k, size_t *kb); int pmemkv_iterator_read_range(pmemkv_iterator *it, size_t pos, size_t n, const char **data, size_t *rb); int pmemkv_write_iterator_write_range(pmemkv_write_iterator *it, size_t pos, size_t n, char **data, size_t *wb); int pmemkv_write_iterator_commit(pmemkv_write_iterator *it); void pmemkv_write_iterator_abort(pmemkv_write_iterator *it); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* LIBPMEMKV_H */ pmemkv-1.5.0/src/libpmemkv.hpp000066400000000000000000002076731410000423300163040ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #ifndef LIBPMEMKV_HPP #define LIBPMEMKV_HPP #include #include #include #include #include #include #include #include #include #include "libpmemkv.h" #include /*! \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 { /** * Partial string_view implementation, defined in pmem::obj namespace * in libpmemobj-cpp library (see: https://pmem.io/libpmemobj-cpp ). */ using string_view = obj::string_view; /** * 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 most of pmemkv functions. Most of functions in libpmemkv API return one of the following status codes. 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 status::UNKNOWN_ERROR, it is possible that in future versions it will return status::INVALID_ARGUMENT. Recommended way to check for an error is to compare status with status::OK (see pmem::kv::db basic example). */ 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) */ COMPARATOR_MISMATCH = PMEMKV_STATUS_COMPARATOR_MISMATCH, /**< db was created with a different comparator */ }; /** * Provides string representation of a status, along with its number * as specified by enum. * * It's useful for debugging, e.g. with pmem::db::errormsg() * @code * std::cout << pmemkv_errormsg() << std::endl; * @endcode */ inline std::ostream &operator<<(std::ostream &os, const status &s) { static const std::string statuses[] = {"OK", "UNKNOWN_ERROR", "NOT_FOUND", "NOT_SUPPORTED", "INVALID_ARGUMENT", "CONFIG_PARSING_ERROR", "CONFIG_TYPE_ERROR", "STOPPED_BY_CB", "OUT_OF_MEMORY", "WRONG_ENGINE_NAME", "TRANSACTION_SCOPE_ERROR", "DEFRAG_ERROR", "COMPARATOR_MISMATCH"}; int status_no = static_cast(s); os << statuses[status_no] << " (" << status_no << ")"; return os; } /*! \exception bad_result_access \brief Defines a type of object to be thrown by result::get_value() when result doesn't contain value. */ class bad_result_access : public std::runtime_error { public: bad_result_access(const char *what_arg) : std::runtime_error(what_arg) { } const char *what() const noexcept final { return std::runtime_error::what(); } private: }; /*! \class result \brief Stores result of an operation. It always contains status and optionally can contain value. If result contains value: is_ok() returns true, get_value() returns value, get_status() returns status::OK. If result contains error: is_ok() returns false, get_value() throws bad_result_access, get_status() returns status other than status::OK. */ template class result { union { T value; }; status s; public: result(const T &val) noexcept(noexcept(T(std::declval()))); result(const status &err) noexcept; result(const result &other) noexcept(noexcept(T(std::declval()))); result(result &&other) noexcept(noexcept(T(std::declval()))); result(T &&val) noexcept(noexcept(T(std::declval()))); ~result(); result & operator=(const result &other) noexcept(noexcept(std::declval().~T()) && noexcept(T(std::declval()))); result &operator=(result &&other) noexcept(noexcept(std::declval().~T()) && noexcept(T(std::declval()))); bool is_ok() const noexcept; const T &get_value() const &; T &get_value() &; T &&get_value() &&; status get_status() const noexcept; }; /** * Creates result with value (status is automatically initialized to status::OK). * * @param[in] val value. */ template result::result(const T &val) noexcept(noexcept(T(std::declval()))) : value(val), s(status::OK) { } /** * Creates result which contains only status. * * @param[in] status status other than status::OK. */ template result::result(const status &status) noexcept : s(status) { assert(s != status::OK); } /** * Default copy constructor. * * @param[in] other result to copy. */ template result::result(const result &other) noexcept(noexcept(T(std::declval()))) : s(other.s) { if (s == status::OK) new (&value) T(other.value); } /** * Default move constructor. * * @param[in] other result to move. */ template result::result(result &&other) noexcept(noexcept(T(std::declval()))) : s(other.s) { if (s == status::OK) new (&value) T(std::move(other.value)); other.s = status::UNKNOWN_ERROR; } /** * Explicit destructor */ template result::~result() { if (s == status::OK) value.~T(); } /** * Default copy assignment operator. * * @param[in] other result to copy. */ template result & result::operator=(const result &other) noexcept(noexcept(std::declval().~T()) && noexcept(T(std::declval()))) { if (s == status::OK && other.is_ok()) value = other.value; else if (other.is_ok()) new (&value) T(other.value); else if (s == status::OK) value.~T(); s = other.s; return *this; } /** * Default move assignment operator. * * @param[in] other result to move. */ template result & result::operator=(result &&other) noexcept(noexcept(std::declval().~T()) && noexcept(T(std::declval()))) { if (s == status::OK && other.is_ok()) value = std::move(other.value); else if (other.is_ok()) new (&value) T(std::move(other.value)); else if (s == status::OK) value.~T(); s = other.s; other.s = status::UNKNOWN_ERROR; return *this; } /** * Constructor with rvalue reference to T. * * @param[in] val rvalue reference to T */ template result::result(T &&val) noexcept(noexcept(T(std::declval()))) : value(std::move(val)), s(status::OK) { } /** * Checks if the result contains value (status == status::OK). * * @return bool */ template bool result::is_ok() const noexcept { return s == status::OK; } /** * Returns const reference to value from the result. * * If result doesn't contain value, throws bad_result_access. * * @throw bad_result_access * * @return const reference to value from the result. */ template const T &result::get_value() const & { if (s == status::OK) return value; else throw bad_result_access("bad_result_access: value doesn't exist"); } /** * Returns reference to value from the result. * * If result doesn't contain value, throws bad_result_access. * * @throw bad_result_access * * @return reference to value from the result */ template T &result::get_value() & { if (s == status::OK) return value; else throw bad_result_access("bad_result_access: value doesn't exist"); } /** * Returns rvalue reference to value from the result. * * If result doesn't contain value, throws bad_result_access. * * @throw bad_result_access * * @return rvalue reference to value from the result */ template T &&result::get_value() && { if (s == status::OK) { s = status::UNKNOWN_ERROR; return std::move(value); } else throw bad_result_access("bad_result_access: value doesn't exist"); } /** * Returns status from the result. * * It returns status::OK if there is a value, and other status (with the appropriate * 'error') if there isn't any value. * * @return status */ template status result::get_status() const noexcept { return s; } template bool operator==(const result &lhs, const status &rhs) { return lhs.get_status() == rhs; } template bool operator==(const status &lhs, const result &rhs) { return lhs == rhs.get_status(); } template bool operator!=(const result &lhs, const status &rhs) { return lhs.get_status() != rhs; } template bool operator!=(const status &lhs, const result &rhs) { return lhs != rhs.get_status(); } /*! \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 { #define force_create_deprecated \ __attribute__((deprecated("use config::put_create_or_error_if_exists instead"))) public: config() noexcept; explicit config(pmemkv_config *cfg) 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 *)) noexcept; template status put_object(const std::string &key, std::unique_ptr object) 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; status put_size(std::uint64_t size) noexcept; status put_path(const std::string &path) noexcept; status put_force_create(bool value) noexcept force_create_deprecated; status put_create_or_error_if_exists(bool value) noexcept; status put_create_if_missing(bool value) noexcept; status put_oid(PMEMoid *oid) noexcept; template status put_comparator(Comparator &&comparator); 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; std::unique_ptr config_; }; /*! \class tx \brief Pmemkv transaction handle. __This API is EXPERIMENTAL and might change.__ The tx class allows grouping put and remove operations into a single atomic action (with respect to persistence and concurrency). Concurrent engines provide transactions with ACID (atomicity, consistency, isolation, durability) properties. Transactions for single threaded engines provide atomicity, consistency and durability. Actions in a transaction are executed in the order in which they were called. __Example__ usage: @snippet examples/pmemkv_transaction_cpp/pmemkv_transaction.cpp transaction */ class tx { public: tx(pmemkv_tx *tx_) noexcept; status put(string_view key, string_view value) noexcept; status remove(string_view key) noexcept; status commit() noexcept; void abort() noexcept; private: std::unique_ptr tx_; }; /*! \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. Note: It does not explicitly provide upper_bound/lower_bound functions. If you want to obtain an element(s) above or below the selected key, you can use pmem::kv::get_above() or pmem::kv::get_below(). See descriptions of these functions for details. __Example__ of basic usage: @snippet examples/pmemkv_basic_cpp/pmemkv_basic.cpp basic __Example__ with existing database: @snippet examples/pmemkv_open_cpp/pmemkv_open.cpp open __Example__ for pmemkv's database supporting multiple engines: @snippet examples/pmemkv_pmemobj_cpp/pmemkv_pmemobj.cpp multiple-engines */ class db { template class iterator; public: using read_iterator = iterator; using write_iterator = iterator; db() noexcept; status open(const std::string &engine_name, config &&cfg = config{}) 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); result tx_begin() noexcept; result new_read_iterator(); result new_write_iterator(); std::string errormsg(); private: std::unique_ptr db_; }; /*! \class db::iterator \brief Iterator provides methods to iterate over records in db. __This API is EXPERIMENTAL and might change.__ It can be only created by methods in db (db::new_read_iterator() - for a read iterator, and db::new_write_iterator() for a write iterator). Both iterator types (write_iterator and read_iterator) allow reading record's key and value. A write_iterator additionally can modify record's value transactionally. Holding simultaneously in the same thread more than one iterator is undefined behavior. __Example__ usage of iterators with single-threaded engines: @snippet examples/pmemkv_iterator_cpp/pmemkv_iterator.cpp single-threaded __Example__ usage of iterators with concurrent engines: @snippet examples/pmemkv_iterator_cpp/pmemkv_iterator.cpp concurrent */ template class db::iterator { using iterator_type = typename std::conditional::type; template class OutputIterator; public: iterator(iterator_type *it); status seek(string_view key) noexcept; status seek_lower(string_view key) noexcept; status seek_lower_eq(string_view key) noexcept; status seek_higher(string_view key) noexcept; status seek_higher_eq(string_view key) noexcept; status seek_to_first() noexcept; status seek_to_last() noexcept; status is_next() noexcept; status next() noexcept; status prev() noexcept; result key() noexcept; result read_range(size_t pos = 0, size_t n = std::numeric_limits::max()) noexcept; template typename std::enable_if>>>::type write_range(size_t pos = 0, size_t n = std::numeric_limits::max()) noexcept; template typename std::enable_if::type commit() noexcept; template typename std::enable_if::type abort() noexcept; private: std::unique_ptr< iterator_type, typename std::conditional::type> it_; pmemkv_iterator *get_raw_it(); }; /*! \class db::iterator::OutputIterator \brief OutputIterator provides iteration through elements without a possibility of reading them. It is only allowed to modify them. */ template template class db::iterator::OutputIterator { struct assign_only; public: using reference = assign_only &; using pointer = void; using difference_type = std::ptrdiff_t; using value_type = void; using iterator_category = std::output_iterator_tag; OutputIterator(T *x); reference operator*(); OutputIterator &operator++(); OutputIterator operator++(int); OutputIterator &operator--(); OutputIterator operator--(int); assign_only operator[](difference_type pos); difference_type operator-(const OutputIterator &other) const; bool operator!=(const OutputIterator &other) const; private: struct assign_only { friend OutputIterator; assign_only(T *x); assign_only &operator=(const T &x); private: T *c; }; assign_only ao; }; template template db::iterator::OutputIterator::OutputIterator(T *x) : ao(x) { } template template typename db::iterator::template OutputIterator::reference db::iterator::OutputIterator::operator*() { return ao; } template template typename db::iterator::template OutputIterator & db::iterator::OutputIterator::operator++() { ao.c += sizeof(T); return *this; } template template typename db::iterator::template OutputIterator db::iterator::OutputIterator::operator++(int) { auto tmp = *this; ++(*this); return tmp; } template template typename db::iterator::template OutputIterator & db::iterator::OutputIterator::operator--() { ao.c -= sizeof(T); return *this; } template template typename db::iterator::template OutputIterator db::iterator::OutputIterator::operator--(int) { auto tmp = *this; --(*this); return tmp; } template template typename db::iterator::template OutputIterator::assign_only db::iterator::OutputIterator::operator[](difference_type pos) { return assign_only(ao.c + pos); } template template typename db::iterator::template OutputIterator::difference_type db::iterator::OutputIterator::operator-(const OutputIterator &other) const { return this->ao.c - other.ao.c; } template template bool db::iterator::OutputIterator::operator!=( const OutputIterator &other) const { return this->ao.c != other.ao.c; } template template db::iterator::OutputIterator::assign_only::assign_only(T *x) : c(x) { } template template typename db::iterator::template OutputIterator::assign_only & db::iterator::OutputIterator::assign_only::operator=(const T &x) { *c = x; return *this; } template <> inline db::iterator::iterator(iterator_type *it) : it_(it, &pmemkv_iterator_delete) { } template <> inline db::iterator::iterator(iterator_type *it) : it_(it, &pmemkv_write_iterator_delete) { } /** * Changes iterator position to the record with given *key*. * If the record is present and no errors occurred, returns pmem::kv::status::OK. If the * record does not exist, pmem::kv::status::NOT_FOUND is returned and the iterator * position is undefined. Other possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @param[in] key key that will be equal to the key of the record on the new iterator * position * * @return pmem::kv::status */ template inline status db::iterator::seek(string_view key) noexcept { return static_cast( pmemkv_iterator_seek(this->get_raw_it(), key.data(), key.size())); } /** * Changes iterator position to the record with key lower than given *key*. * If the record is present and no errors occurred, returns pmem::kv::status::OK. If the * record does not exist, pmem::kv::status::NOT_FOUND is returned and the iterator * position is undefined. Other possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @param[in] key key that will be higher than the key of the record on the new iterator * position * * @return pmem::kv::status */ template inline status db::iterator::seek_lower(string_view key) noexcept { return static_cast( pmemkv_iterator_seek_lower(this->get_raw_it(), key.data(), key.size())); } /** * Changes iterator position to the record with key equal or lower than given *key*. * If the record is present and no errors occurred, returns pmem::kv::status::OK. If the * record does not exist, pmem::kv::status::NOT_FOUND is returned and the iterator * position is undefined. Other possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @param[in] key key that will be equal or higher than the key of the record on the new * iterator position * * @return pmem::kv::status */ template inline status db::iterator::seek_lower_eq(string_view key) noexcept { return static_cast(pmemkv_iterator_seek_lower_eq(this->get_raw_it(), key.data(), key.size())); } /** * Changes iterator position to the record with key higher than given *key*. * If the record is present and no errors occurred, returns pmem::kv::status::OK. If the * record does not exist, pmem::kv::status::NOT_FOUND is returned and the iterator * position is undefined. Other possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @param[in] key key that will be lower than the key of the record on the new iterator * position * * @return pmem::kv::status */ template inline status db::iterator::seek_higher(string_view key) noexcept { return static_cast( pmemkv_iterator_seek_higher(this->get_raw_it(), key.data(), key.size())); } /** * Changes iterator position to the record with key equal or higher than given *key*. * If the record is present and no errors occurred, returns pmem::kv::status::OK. If the * record does not exist, pmem::kv::status::NOT_FOUND is returned and the iterator * position is undefined. Other possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @param[in] key key that will be equal or lower than the key of the record on the new * iterator position * * @return pmem::kv::status */ template inline status db::iterator::seek_higher_eq(string_view key) noexcept { return static_cast(pmemkv_iterator_seek_higher_eq( this->get_raw_it(), key.data(), key.size())); } /** * Changes iterator position to the first record. * If db isn't empty, and no errors occurred, returns * pmem::kv::status::OK. If db is empty, pmem::kv::status::NOT_FOUND is returned * and the iterator position is undefined. Other possible return values are described in * pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @return pmem::kv::status */ template inline status db::iterator::seek_to_first() noexcept { return static_cast(pmemkv_iterator_seek_to_first(this->get_raw_it())); } /** * Changes iterator position to the last record. * If db isn't empty, and no errors occurred, returns * pmem::kv::status::OK. If db is empty, pmem::kv::status::NOT_FOUND is returned * and the iterator position is undefined. Other possible return values are described in * pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * @return pmem::kv::status */ template inline status db::iterator::seek_to_last() noexcept { return static_cast(pmemkv_iterator_seek_to_last(this->get_raw_it())); } /** * Checks if there is a next record available. If true is returned, it is guaranteed that * iterator.next() will return status::OK, otherwise iterator is already on the last * element and iterator.next() will return status::NOT_FOUND. * * If the iterator is on an undefined position, calling this method is undefined * behaviour. * * @return bool */ template inline status db::iterator::is_next() noexcept { return static_cast(pmemkv_iterator_is_next(this->get_raw_it())); } /** * Changes iterator position to the next record. * If the next record exists, returns pmem::kv::status::OK, otherwise * pmem::kv::status::NOT_FOUND is returned and the iterator position is undefined. Other * possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * If the iterator is on an undefined position, calling this method is undefined * behaviour. * * @return pmem::kv::status */ template inline status db::iterator::next() noexcept { return static_cast(pmemkv_iterator_next(this->get_raw_it())); } /** * Changes iterator position to the previous record. * If the previous record exists, returns pmem::kv::status::OK, otherwise * pmem::kv::status::NOT_FOUND is returned and the iterator position is undefined. Other * possible return values are described in pmem::kv::status. * * It internally aborts all changes made to an element previously pointed by the iterator. * * If the iterator is on an undefined position, calling this method is undefined * behaviour. * * @return pmem::kv::status */ template inline status db::iterator::prev() noexcept { return static_cast(pmemkv_iterator_prev(this->get_raw_it())); } /** * Returns record's key (pmem::kv::string_view), in * pmem::kv::result. * * If the iterator is on an undefined position, calling this method is undefined * behaviour. * * @return pmem::kv::result */ template inline result db::iterator::key() noexcept { const char *c; size_t size; auto s = static_cast(pmemkv_iterator_key(this->get_raw_it(), &c, &size)); if (s == status::OK) return {string_view{c, size}}; else return {s}; } /** * Returns value's range (pmem::kv::string_view) to read, in pmem::kv::result. * * It is only used to read a value. If you want to modify the value, use * db::iterator::write_range instead. * * If the iterator is on an undefined position, calling this method is undefined * behaviour. * * @param[in] pos position of the element in a value which will be the first element in * the returned range (default = 0) * @param[in] n number of elements in range (default = std::numeric_limits::max(), * if n is bigger than the length of a value it's automatically shrunk) * * @return pmem::kv::result */ template inline result db::iterator::read_range(size_t pos, size_t n) noexcept { const char *data; size_t size; auto s = static_cast( pmemkv_iterator_read_range(this->get_raw_it(), pos, n, &data, &size)); if (s == status::OK) return {string_view{data, size}}; else return {s}; } /** * Returns value's range (pmem::obj::slice>) to modify, * in pmem::kv::result. * * It is only used to modify a value. If you want to read the value, use * db::iterator::read_range instead. * * Changes made on a requested range are not persistent until db::iterator::commit is * called. * * If iterator is on an undefined position, calling this method is undefined behaviour. * * @param[in] pos position of the element in a value which will be the first element in * the returned range (default = 0) * @param[in] n number of elements in range (default = std::numeric_limits::max(), * if n is bigger than the length of a value it's automatically shrunk) * * @return pmem::kv::result>> */ template <> template <> inline result::OutputIterator>> db::iterator::write_range(size_t pos, size_t n) noexcept { char *data; size_t size; auto s = static_cast( pmemkv_write_iterator_write_range(this->it_.get(), pos, n, &data, &size)); if (s == status::OK) { try { return {{data, data + size}}; } catch (std::out_of_range &e) { return {status::INVALID_ARGUMENT}; } } else return {s}; } /** * Commits modifications made on the current record. * * Calling this method is the only way to save modifications made by the iterator on the * current record. You need to call this method before changing the iterator position, * otherwise modifications will be automatically aborted. * * @return pmem::kv::status */ template <> template <> inline status db::iterator::commit() noexcept { auto s = static_cast(pmemkv_write_iterator_commit(this->it_.get())); return s; } /** * Aborts uncommitted modifications made on the current record. */ template <> template <> inline void db::iterator::abort() noexcept { pmemkv_write_iterator_abort(this->it_.get()); } template <> inline pmemkv_iterator *db::iterator::get_raw_it() { return it_.get(); } template <> inline pmemkv_iterator *db::iterator::get_raw_it() { return it_.get()->iter; } /*! \namespace pmem::kv::internal \brief Internal pmemkv classes for C++ API Nothing from this namespace should be used by the users. It holds pmemkv internal classes which might be changed or removed in future. */ namespace internal { /* * Abstracts unique_ptr - exposes only void *get() method and a destructor. * This class is needed for C callbacks which cannot be templated * (type of object and deleter must be abstracted away). */ struct unique_ptr_wrapper_base { virtual ~unique_ptr_wrapper_base() { } virtual void *get() = 0; }; template struct unique_ptr_wrapper : public unique_ptr_wrapper_base { unique_ptr_wrapper(std::unique_ptr ptr) : ptr(std::move(ptr)) { } void *get() override { return ptr.get(); } std::unique_ptr ptr; }; class comparator_base { public: virtual ~comparator_base() { } virtual int compare(string_view key1, string_view key2) = 0; }; template struct comparator_wrapper : public comparator_base { comparator_wrapper(const Comparator &cmp) : cmp(cmp) { } comparator_wrapper(Comparator &&cmp) : cmp(std::move(cmp)) { } int compare(string_view key1, string_view key2) override { return cmp.compare(key1, key2); } Comparator cmp; }; struct comparator_config_entry : public unique_ptr_wrapper_base { comparator_config_entry( std::unique_ptr ptr, std::unique_ptr c_cmp) : ptr(std::move(ptr)), c_cmp(std::move(c_cmp)) { } void *get() override { return c_cmp.get(); } std::unique_ptr ptr; std::unique_ptr c_cmp; }; /* * 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 void call_up_destructor(void *object) { auto *ptr = static_cast(object); delete ptr; } static inline void *call_up_get(void *object) { auto *ptr = static_cast(object); return ptr->get(); } static inline int call_comparator_function(const char *k1, size_t kb1, const char *k2, size_t kb2, void *arg) { auto *cmp = static_cast(arg); return cmp->compare(string_view(k1, kb1), string_view(k2, kb2)); } } /* extern "C" */ } /* namespace internal */ /** * Default constructor with uninitialized config. */ inline config::config() noexcept : config_(nullptr, &pmemkv_config_delete) { } /** * Creates config from pointer to pmemkv_config. * Ownership is transferred to config class. */ inline config::config(pmemkv_config *cfg) noexcept : config_(cfg, &pmemkv_config_delete) { } /** * 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_.get() == nullptr) { this->config_ = {pmemkv_config_new(), &pmemkv_config_delete}; if (this->config_.get() == 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_.get(), 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_.get(), key.data(), (void *)value, deleter)); } /** * Puts unique_ptr (to an object) to a config. * * @param[in] key The string representing config item's name. * @param[in] object unique_ptr to an object. * * @return pmem::kv::status */ template inline status config::put_object(const std::string &key, std::unique_ptr object) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; internal::unique_ptr_wrapper_base *wrapper; try { wrapper = new internal::unique_ptr_wrapper(std::move(object)); } catch (std::bad_alloc &e) { return status::OUT_OF_MEMORY; } catch (...) { return status::UNKNOWN_ERROR; } return static_cast(pmemkv_config_put_object_cb( this->config_.get(), key.data(), (void *)wrapper, internal::call_up_get, internal::call_up_destructor)); } /** * Puts comparator object to a config. * * Comparator must: * - implement `int compare(pmem::kv::string_view, pmem::kv::string_view)` * - implement `std::string name()` * - be copy or move constructible * - be thread-safe * * @param[in] comparator forwarding reference to a comparator * * @return pmem::kv::status * * __Example__ implementation of custom comparator: * @snippet examples/pmemkv_comparator_cpp/pmemkv_comparator.cpp custom-comparator * * And __example__ usage (set in config and use while itarating over keys): * @snippet examples/pmemkv_comparator_cpp/pmemkv_comparator.cpp comparator-usage */ template inline status config::put_comparator(Comparator &&comparator) { static_assert( std::is_same().compare( std::declval(), std::declval())), int>::value, "Comparator should implement `int compare(pmem::kv::string_view, pmem::kv::string_view)` method"); static_assert(std::is_convertible().name()), std::string>::value, "Comparator should implement `std::string name()` method"); std::unique_ptr wrapper; try { wrapper = std::unique_ptr( new internal::comparator_wrapper( std::forward(comparator))); } catch (std::bad_alloc &e) { return status::OUT_OF_MEMORY; } catch (...) { return status::UNKNOWN_ERROR; } auto cmp = std::unique_ptr( pmemkv_comparator_new(&internal::call_comparator_function, std::string(comparator.name()).c_str(), wrapper.get()), &pmemkv_comparator_delete); if (cmp == nullptr) return status::UNKNOWN_ERROR; internal::unique_ptr_wrapper_base *entry; try { entry = new internal::comparator_config_entry(std::move(wrapper), std::move(cmp)); } catch (std::bad_alloc &e) { return status::OUT_OF_MEMORY; } catch (...) { return status::UNKNOWN_ERROR; } return static_cast(pmemkv_config_put_object_cb( this->config_.get(), "comparator", (void *)entry, internal::call_up_get, internal::call_up_destructor)); } /** * 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_.get(), 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_.get(), 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_.get(), key.data(), value.data())); } /** * Puts size to a config, it's required when creating new database pool. * * @param[in] size of the database in bytes. * * @return pmem::kv::status */ inline status config::put_size(std::uint64_t size) noexcept { return put_uint64("size", size); } /** * Puts path (of a database pool) to a config, to open or create. * * @param[in] path to a database file or to a poolset file (see **poolset**(5) for * details). Note that when using poolset file, size should be 0. * * @return pmem::kv::status */ inline status config::put_path(const std::string &path) noexcept { return put_string("path", path); } /** * It's an alias for config::put_create_or_error_if_exists, kept for compatibility. * * @deprecated use config::put_create_or_error_if_exists instead. * @return pmem::kv::status */ inline status config::put_force_create(bool value) noexcept { return put_create_or_error_if_exists(value); } /** * Puts create_or_error_if_exists parameter to a config. This flag is mutually exclusive * with **create_if_missing** (see config::put_create_if_missing). * It works only with engines supporting this flag and it means: * If true: pmemkv creates the pool, unless it exists - then it fails. * If false: pmemkv opens the pool, unless the path does not exist - then it fails. * False by default. * * @return pmem::kv::status */ inline status config::put_create_or_error_if_exists(bool value) noexcept { return put_uint64("create_or_error_if_exists", static_cast(value)); } /** * Puts create_if_missing parameter to a config. This flag is mutually exclusive * with **create_or_error_if_exists** (see config::put_create_or_error_if_exists). * It works only with engines supporting this flag and it means: * If true: pmemkv tries to open the pool and if that doesn't succeed * it means there's (most likely) no pool to use, so it creates it. * If false: pmemkv opens the pool, unless the path does not exist - then it fails. * False by default. * * @return pmem::kv::status */ inline status config::put_create_if_missing(bool value) noexcept { return put_uint64("create_if_missing", static_cast(value)); } /** * Puts PMEMoid object to a config. * * @param[in] oid pointer (for details see **libpmemobj**(7)) which points to the engine * data. If oid is null, engine will allocate new data, otherwise it will use existing * one. * * @return pmem::kv::status */ inline status config::put_oid(PMEMoid *oid) noexcept { if (init() != 0) return status::UNKNOWN_ERROR; return static_cast(pmemkv_config_put_oid(this->config_.get(), oid)); } /** * 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_.get() == nullptr) return status::NOT_FOUND; std::size_t size; auto s = static_cast(pmemkv_config_get_data( this->config_.get(), 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_.get() == nullptr) return status::NOT_FOUND; auto s = static_cast(pmemkv_config_get_object( this->config_.get(), 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_.get() == nullptr) return status::NOT_FOUND; return static_cast( pmemkv_config_get_uint64(this->config_.get(), 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_.get() == nullptr) return status::NOT_FOUND; return static_cast( pmemkv_config_get_int64(this->config_.get(), 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_.get() == nullptr) return status::NOT_FOUND; const char *data; auto s = static_cast( pmemkv_config_get_string(this->config_.get(), 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 { return this->config_.release(); } /** * Constructs C++ tx object from a C pmemkv_tx pointer */ inline tx::tx(pmemkv_tx *tx_) noexcept : tx_(tx_, &pmemkv_tx_end) { } /** * Removes from database record with given *key*. The removed element is still * visible until commit. This function will succeed even if there is no element in the * database. * * @param[in] key record's key to query for, to be removed * * @return pmem::kv::status */ inline status tx::remove(string_view key) noexcept { return static_cast(pmemkv_tx_remove(tx_.get(), key.data(), key.size())); } /** * Inserts a key-value pair into pmemkv database. The inserted elements are not * visible (not even in the same thread) until commit. * * @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 tx::put(string_view key, string_view value) noexcept { return static_cast(pmemkv_tx_put(tx_.get(), key.data(), key.size(), value.data(), value.size())); } /** * Commits the transaction. All operations of this transaction are applied as * a single power fail-safe atomic action. The tx object can be safely used after * commit. * * @return pmem::kv::status */ inline status tx::commit() noexcept { return static_cast(pmemkv_tx_commit(tx_.get())); } /** * Aborts the transaction. The tx object can be safely used after * abort. */ inline void tx::abort() noexcept { pmemkv_tx_abort(tx_.get()); } /* * 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 : db_(nullptr, &pmemkv_close) { } /** * 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 { pmemkv_db *db; auto s = static_cast(pmemkv_open(engine_name.c_str(), cfg.release(), &db)); if (s == pmem::kv::status::OK) this->db_.reset(db); return s; } /** * Closes pmemkv database. */ inline void db::close() noexcept { this->db_.reset(nullptr); } /** * 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_.get(), &cnt)); } /** * It returns number of currently stored elements in pmem::kv::db, whose keys * are greater than the given *key*. * Keys are sorted in order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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_.get(), 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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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 order specified by a comparator. * * @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_.get(), 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_.get(), 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_.get(), 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_.get(), 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_.get(), 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_.get(), 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_.get(), 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_.get(), start_percent, amount_percent)); } /** * Returns new write iterator in pmem::kv::result. * * @return pmem::kv::result */ inline result db::new_write_iterator() { pmemkv_write_iterator *tmp; auto ret = static_cast(pmemkv_write_iterator_new(db_.get(), &tmp)); if (static_cast(ret) == status::OK) return {db::iterator{tmp}}; else return {ret}; } /** * Returns new read iterator in pmem::kv::result. * * @return pmem::kv::result */ inline result db::new_read_iterator() { pmemkv_iterator *tmp; auto ret = static_cast(pmemkv_iterator_new(db_.get(), &tmp)); if (ret == status::OK) return {db::iterator{tmp}}; else return {ret}; } /** * Returns a human readable string describing the last error. * Even if this is a method from the db class, it can return the last error from * some other class. * * @return std::string with a description of the last error */ inline std::string db::errormsg() { return std::string(pmemkv_errormsg()); } /** * Returns a human readable string describing the last error. * * @return std::string with a description of the last error */ static inline std::string errormsg() { return std::string(pmemkv_errormsg()); } /** * Starts a pmemkv transaction. * * @return transaction handle */ inline result db::tx_begin() noexcept { pmemkv_tx *tx_; auto s = static_cast(pmemkv_tx_begin(db_.get(), &tx_)); if (s == status::OK) return result(tx(tx_)); else return result(s); } } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_HPP */ pmemkv-1.5.0/src/libpmemkv.map000066400000000000000000000035601410000423300162570ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # # 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_object_cb; pmemkv_config_put_string; pmemkv_config_put_uint64; pmemkv_config_put_size; pmemkv_config_put_path; pmemkv_config_put_oid; pmemkv_config_put_comparator; pmemkv_config_put_create_if_missing; pmemkv_config_put_create_or_error_if_exists; pmemkv_config_put_force_create; pmemkv_comparator_new; pmemkv_comparator_delete; 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_iterator_delete; pmemkv_iterator_is_next; pmemkv_iterator_key; pmemkv_iterator_new; pmemkv_iterator_next; pmemkv_iterator_prev; pmemkv_iterator_read_range; pmemkv_iterator_seek; pmemkv_iterator_seek_higher; pmemkv_iterator_seek_higher_eq; pmemkv_iterator_seek_lower; pmemkv_iterator_seek_lower_eq; pmemkv_iterator_seek_to_first; pmemkv_iterator_seek_to_last; pmemkv_open; pmemkv_put; pmemkv_remove; pmemkv_tx_abort; pmemkv_tx_begin; pmemkv_tx_commit; pmemkv_tx_end; pmemkv_tx_put; pmemkv_tx_remove; pmemkv_write_iterator_abort; pmemkv_write_iterator_commit; pmemkv_write_iterator_delete; pmemkv_write_iterator_new; pmemkv_write_iterator_write_range; local: *; }; pmemkv-1.5.0/src/libpmemkv_json_config.cc000066400000000000000000000063641410000423300204520ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #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; try { if (!config) { throw std::runtime_error("Config has to be specified"); } if (!json) { throw std::runtime_error( "Configuration json has to be specified"); } 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 with error: " + std::string(pmemkv_errormsg())); } 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 with error: " + std::string(pmemkv_errormsg())); } 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 with error: " + std::string(pmemkv_errormsg())); } 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( "Parsing subconfig failed with error: " + std::string( pmemkv_config_from_json_errormsg())); } 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 with error: " + std::string(pmemkv_errormsg())); } 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.5.0/src/libpmemkv_json_config.h000066400000000000000000000006261410000423300203070ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019, Intel Corporation */ #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.5.0/src/libpmemkv_json_config.map000066400000000000000000000004231410000423300206300ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Intel Corporation # # # 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.5.0/src/out.cc000066400000000000000000000013101410000423300146770ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include "out.h" #include "libpmemkv.h" #include "libpmemkv.h" #include #include static thread_local std::stringstream error_stream; static thread_local std::string str; static thread_local int last_status; std::ostream &out_err_stream(const char *func) { error_stream.str(std::string()); error_stream << "[" << func << "] "; return error_stream; } void set_last_status(int s) { last_status = s; } const char *out_get_errormsg(void) { if (last_status == PMEMKV_STATUS_NOT_FOUND || last_status == PMEMKV_STATUS_STOPPED_BY_CB) return ""; str = error_stream.str(); return str.c_str(); } pmemkv-1.5.0/src/out.h000066400000000000000000000012721410000423300145500ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #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); void set_last_status(int status); #endif /* LIBPMEMKV_OUT_H */ pmemkv-1.5.0/src/pmemobj_engine.h000066400000000000000000000064531410000423300167250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #ifndef LIBPMEMKV_PMEMOBJ_ENGINE_H #define LIBPMEMKV_PMEMOBJ_ENGINE_H #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(const std::unique_ptr &cfg, const std::string &layout) { const char *path = nullptr; 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 create_or_error_if_exists = 0; uint64_t create_if_missing = 0; cfg_by_path = true; cfg->get_uint64("create_if_missing", &create_if_missing); if (!cfg->get_uint64("create_or_error_if_exists", &create_or_error_if_exists)) { /* 'force_create' is here for compatibility with bindings, * which may still use this flag in their API */ cfg->get_uint64("force_create", &create_or_error_if_exists); } if (create_if_missing && create_or_error_if_exists) { throw internal::invalid_argument( "Both flags set in config: \"create_if_missing\" and \"create_or_error_if_exists\""); } if (create_if_missing || create_or_error_if_exists) { bool failed_open = false; if (!create_or_error_if_exists) try { pmpool = pmem::obj::pool::open( path, layout); } catch (pmem::pool_invalid_argument &e) { failed_open = true; } if (failed_open || create_or_error_if_exists) { auto size = cfg->get_size(); pmpool = create_or_fail(path, size, layout); } } else { /* no flags set, just open */ try { pmpool = pmem::obj::pool::open(path, layout); } catch (pmem::pool_invalid_argument &e) { throw internal::invalid_argument(e.what()); } } root_oid = static_cast>(pmpool) .root() ->ptr.raw_ptr(); } else if (is_oid) { pmpool = pmem::obj::pool_base(pmemobj_pool_by_ptr(oid)); root_oid = oid; } } ~pmemobj_engine_base() { if (cfg_by_path) { try { pmpool.close(); } catch (const std::logic_error &e) { std::terminate(); } } } protected: struct Root { /* field ptr used when path is specified */ pmem::obj::persistent_ptr ptr; }; pmem::obj::pool_base pmpool; PMEMoid *root_oid; bool cfg_by_path = false; private: pmem::obj::pool create_or_fail(const char *path, const std::size_t size, const std::string &layout) { try { return pmem::obj::pool::create(path, layout, size, S_IRWXU); } catch (pmem::pool_invalid_argument &e) { throw internal::invalid_argument(e.what()); } } }; } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_PMEMOBJ_ENGINE_H */ pmemkv-1.5.0/src/polymorphic_string.h000066400000000000000000000046601410000423300177000ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2020, Intel Corporation */ #pragma once #include #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)...); } pmem::obj::slice range(size_t p, size_t n) { return pstr.range(p, n); } 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.5.0/src/transaction.h000066400000000000000000000033221410000423300162640ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #ifndef LIBPMEMKV_TRANSACTION_H #define LIBPMEMKV_TRANSACTION_H #include "libpmemkv.hpp" #include namespace pmem { namespace kv { namespace internal { class transaction { public: transaction() { } virtual ~transaction() { } virtual status put(string_view key, string_view value) = 0; virtual status commit() = 0; virtual void abort() = 0; virtual status remove(string_view key) { return status::NOT_SUPPORTED; } }; class dram_log { public: using element_type = std::pair; void insert(string_view key, string_view value) { op_type.emplace_back(operation::insert); log.emplace_back(std::piecewise_construct, std::forward_as_tuple(key.data(), key.size()), std::forward_as_tuple(value.data(), value.size())); } void remove(string_view key) { op_type.emplace_back(operation::remove); log.emplace_back(std::piecewise_construct, std::forward_as_tuple(key.data(), key.size()), std::forward_as_tuple()); } template void foreach (F1 &&insert_cb, F2 && remove_cb) { assert(op_type.size() == log.size()); for (size_t i = 0; i < log.size(); i++) { switch (op_type[i]) { case operation::insert: insert_cb(log[i]); break; case operation::remove: remove_cb(log[i]); break; default: assert(false); break; } } } void clear() { op_type.clear(); log.clear(); } private: enum class operation { insert, remove }; std::vector op_type; std::vector log; }; } /* namespace internal */ } /* namespace kv */ } /* namespace pmem */ #endif /* LIBPMEMKV_TRANSACTION_H */ pmemkv-1.5.0/src/valgrind/000077500000000000000000000000001410000423300153745ustar00rootroot00000000000000pmemkv-1.5.0/src/valgrind/README000066400000000000000000000001331410000423300162510ustar00rootroot00000000000000Files in this directory were imported from Valgrind 3.14: https://github.com/pmem/valgrind pmemkv-1.5.0/src/valgrind/drd.h000066400000000000000000000547061410000423300163320ustar00rootroot00000000000000/* ---------------------------------------------------------------- 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.5.0/src/valgrind/helgrind.h000066400000000000000000001137331410000423300173510ustar00rootroot00000000000000/* ---------------------------------------------------------------- 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.5.0/src/valgrind/memcheck.h000066400000000000000000000343521410000423300173300ustar00rootroot00000000000000 /* ---------------------------------------------------------------- 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.5.0/src/valgrind/pmemcheck.h000066400000000000000000000245511410000423300175100ustar00rootroot00000000000000/* * 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.5.0/src/valgrind/valgrind.h000066400000000000000000013752211410000423300173660ustar00rootroot00000000000000/* -*- 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.5.0/tests/000077500000000000000000000000001410000423300141415ustar00rootroot00000000000000pmemkv-1.5.0/tests/CMakeLists.txt000066400000000000000000002172031410000423300167060ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # tests/CMakeLists.txt - prepares all the tests; it specifies which tests are enabled based on # options and available programs. Build tests with 'make tests' command, execute them using 'ctest' # include(ctest_helpers.cmake) # ----------------------------------------------------------------- # ## Setup # ----------------------------------------------------------------- # add_custom_target(tests) # Find valgrind if(PKG_CONFIG_FOUND) pkg_check_modules(VALGRIND QUIET valgrind) else() find_package(VALGRIND QUIET) endif() if(NOT VALGRIND_FOUND AND TESTS_USE_VALGRIND) message(FATAL_ERROR "Valgrind not found, but flag TESTS_USE_VALGRIND was set.") elseif(NOT VALGRIND_FOUND) message(WARNING "Valgrind not found. Valgrind tests will not be performed.") elseif(VALGRIND_FOUND) message(STATUS "Found Valgrind in '${VALGRIND_LIBRARY_DIRS}' (version: ${VALGRIND_VERSION})") endif() # Find libpmem & libpmemobj if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ REQUIRED libpmemobj>=${LIBPMEMOBJ_REQUIRED_VERSION}) pkg_check_modules(LIBPMEM REQUIRED libpmem>=${LIBPMEM_REQUIRED_VERSION}) else() find_package(LIBPMEMOBJ REQUIRED ${LIBPMEMOBJ_REQUIRED_VERSION}) find_package(LIBPMEM REQUIRED ${LIBPMEM_REQUIRED_VERSION}) endif() find_pmemcheck() find_libunwind() find_pmreorder() find_pmempool() find_gdb() # Add checks when DEVELOPER_MODE is ON add_cppstyle(tests ${CMAKE_CURRENT_SOURCE_DIR}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/c_api/*.c ${CMAKE_CURRENT_SOURCE_DIR}/common/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/common/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/compatibility/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/comparator/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/config/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/all/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/concurrent/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/memkind/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/persistent/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/pmemobj/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/pmemobj/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/pmreorder/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/sorted/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/sorted/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/transaction/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/result/*.c*) add_check_whitespace(tests ${CMAKE_CURRENT_SOURCE_DIR}/*.* ${CMAKE_CURRENT_SOURCE_DIR}/c_api/*.* ${CMAKE_CURRENT_SOURCE_DIR}/cmake/*.* ${CMAKE_CURRENT_SOURCE_DIR}/common/*.* ${CMAKE_CURRENT_SOURCE_DIR}/compatibility/*.* ${CMAKE_CURRENT_SOURCE_DIR}/comparator/*.* ${CMAKE_CURRENT_SOURCE_DIR}/config/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/all/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/concurrent/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/memkind/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/persistent/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/pmemobj/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/pmreorder/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/sorted/*.* ${CMAKE_CURRENT_SOURCE_DIR}/engine_scenarios/transaction/*.* ${CMAKE_CURRENT_SOURCE_DIR}/result/*.*) if(TESTS_JSON AND NOT BUILD_JSON_CONFIG) message(FATAL_ERROR "Tests requiring 'libpmemkv_json_config' library " "are enabled (with TESTS_JSON option), but the library is not built. " "If you want to run them use BUILD_JSON_CONFIG=ON option.") elseif(NOT TESTS_JSON) message(WARNING "Most of pmemkv's tests require the 'libpmemkv_json_config' library. " "To enable these tests use TESTS_JSON option (and turn BUILD_JSON_CONFIG on).") endif() add_library(test_backtrace STATIC common/test_backtrace.c) if(LIBUNWIND_FOUND) target_compile_definitions(test_backtrace PUBLIC USE_LIBUNWIND=1) endif() add_executable(check_is_pmem common/check_is_pmem.cpp) target_link_libraries(check_is_pmem ${LIBPMEM_LIBRARIES}) if(COVERAGE AND VALGRIND_FOUND) message(STATUS "This is the Coverage build, skipping Valgrind tests") endif() # ----------------------------------------------------------------- # ## Common tests # ----------------------------------------------------------------- # build_test(wrong_engine_name_test wrong_engine_name_test.cc) add_test_generic(NAME wrong_engine_name_test TRACERS none) build_test(error_msg_test error_msg_test.cc) add_test_generic(NAME error_msg_test TRACERS none) if(BUILD_EXAMPLES AND ENGINE_CMAP) add_dependencies(tests example-pmemkv_basic_c example-pmemkv_basic_cpp example-pmemkv_pmemobj_cpp) add_test_generic(NAME example-pmemkv_basic_c TRACERS none) add_test_generic(NAME example-pmemkv_basic_cpp TRACERS none) add_test_generic(NAME example-pmemkv_pmemobj_cpp TRACERS none) if(BUILD_JSON_CONFIG) add_dependencies(tests example-pmemkv_config_c) add_test_generic(NAME example-pmemkv_config_c TRACERS none) endif() 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() build_test(config_c config/config_c.c) add_test_generic(NAME config_c TRACERS none memcheck) build_test(config_cpp config/config_cpp.cc) add_test_generic(NAME config_cpp TRACERS none memcheck) build_test_ext(NAME json_to_config SRC_FILES config/json_to_config.cc LIBS json) add_test_generic(NAME json_to_config TRACERS none memcheck) # Separate binaries for config tests using deprecated functions build_test_ext(NAME deprecated_config_c SRC_FILES config/deprecated_config.c LIBS json OPTS -Wno-deprecated-declarations) add_test_generic(NAME deprecated_config_c TRACERS none memcheck) build_test_ext(NAME deprecated_config_cpp SRC_FILES config/deprecated_config.cc LIBS json OPTS -Wno-deprecated-declarations) add_test_generic(NAME deprecated_config_cpp TRACERS none memcheck) build_test(result result/result.cpp) add_test_generic(NAME result TRACERS none memcheck) # ----------------------------------------------------------------- # ## Test scenarios (parametrized at least with engine name) # ----------------------------------------------------------------- # # Tests for all engines build_test(open engine_scenarios/all/open.cc) build_test_ext(NAME put_get_remove SRC_FILES engine_scenarios/all/put_get_remove.cc LIBS json) build_test_ext(NAME put_get_remove_not_aligned SRC_FILES engine_scenarios/all/put_get_remove_not_aligned.cc LIBS json) build_test_ext(NAME put_get_remove_charset_params SRC_FILES engine_scenarios/all/put_get_remove_charset_params.cc LIBS json) build_test_ext(NAME put_get_remove_long_key SRC_FILES engine_scenarios/all/put_get_remove_long_key.cc LIBS json) build_test_ext(NAME put_get_remove_params SRC_FILES engine_scenarios/all/put_get_remove_params.cc LIBS json) build_test_ext(NAME put_get_std_map SRC_FILES engine_scenarios/all/put_get_std_map.cc LIBS json) build_test_ext(NAME iterate SRC_FILES engine_scenarios/all/iterate.cc LIBS json) build_test_ext(NAME error_handling_oom SRC_FILES engine_scenarios/all/error_handling_oom.cc LIBS json) # Tests for concurrent engines build_test_ext(NAME concurrent_iterate_params SRC_FILES engine_scenarios/concurrent/iterate_params.cc LIBS json) build_test_ext(NAME concurrent_put_get_remove_params SRC_FILES engine_scenarios/concurrent/put_get_remove_params.cc LIBS json) build_test_ext(NAME concurrent_put_get_remove_gen_params SRC_FILES engine_scenarios/concurrent/put_get_remove_gen_params.cc LIBS json) build_test_ext(NAME concurrent_put_get_remove_single_op_params SRC_FILES engine_scenarios/concurrent/put_get_remove_single_op_params.cc LIBS json) build_test_ext(NAME iterator_concurrent SRC_FILES engine_scenarios/concurrent/iterator_concurrent.cc LIBS json) # Tests for persistent engines build_test_ext(NAME persistent_not_found_verify SRC_FILES engine_scenarios/persistent/not_found_verify.cc LIBS json) build_test_ext(NAME persistent_overwrite_verify SRC_FILES engine_scenarios/persistent/overwrite_verify.cc LIBS json) build_test_ext(NAME persistent_put_remove_verify SRC_FILES engine_scenarios/persistent/put_remove_verify.cc LIBS json) build_test_ext(NAME persistent_put_verify_asc_params SRC_FILES engine_scenarios/persistent/put_verify_asc_params.cc LIBS json) build_test_ext(NAME persistent_put_verify_desc_params SRC_FILES engine_scenarios/persistent/put_verify_desc_params.cc LIBS json) build_test_ext(NAME persistent_put_verify SRC_FILES engine_scenarios/persistent/put_verify.cc LIBS json) build_test_ext(NAME persistent_put_get_std_map_multiple_reopen SRC_FILES engine_scenarios/persistent/put_get_std_map_multiple_reopen.cc LIBS json) build_test_ext(NAME pmreorder_insert SRC_FILES engine_scenarios/pmreorder/insert.cc LIBS json) build_test_ext(NAME pmreorder_erase SRC_FILES engine_scenarios/pmreorder/erase.cc LIBS json) build_test_ext(NAME pmreorder_iterator SRC_FILES engine_scenarios/pmreorder/iterator.cc LIBS json) build_test_ext(NAME pmreorder_recover SRC_FILES engine_scenarios/pmreorder/recover.cc LIBS json) # Tests for sorted engines build_test_ext(NAME sorted_iterate SRC_FILES engine_scenarios/sorted/iterate.cc LIBS json) build_test_ext(NAME sorted_get_all_gen_params SRC_FILES engine_scenarios/sorted/get_all_gen_params.cc LIBS json) build_test_ext(NAME sorted_get_above_gen_params SRC_FILES engine_scenarios/sorted/get_above_gen_params.cc LIBS json) build_test_ext(NAME sorted_get_equal_above_gen_params SRC_FILES engine_scenarios/sorted/get_equal_above_gen_params.cc LIBS json) build_test_ext(NAME sorted_get_below_gen_params SRC_FILES engine_scenarios/sorted/get_below_gen_params.cc LIBS json) build_test_ext(NAME sorted_get_equal_below_gen_params SRC_FILES engine_scenarios/sorted/get_equal_below_gen_params.cc LIBS json) build_test_ext(NAME sorted_get_between_gen_params SRC_FILES engine_scenarios/sorted/get_between_gen_params.cc LIBS json) # Tests for pmemobj engines build_test_ext(NAME pmemobj_error_handling_create SRC_FILES engine_scenarios/pmemobj/error_handling_create.cc LIBS json) build_test_ext(NAME pmemobj_error_handling_defrag SRC_FILES engine_scenarios/pmemobj/error_handling_defrag.cc LIBS json) build_test_ext(NAME pmemobj_error_handling_tx_path SRC_FILES engine_scenarios/pmemobj/error_handling_tx_path.cc LIBS json) build_test_ext(NAME pmemobj_put_get_std_map_defrag SRC_FILES engine_scenarios/pmemobj/put_get_std_map_defrag.cc LIBS json) build_test_ext(NAME pmemobj_error_handling_tx_oom SRC_FILES engine_scenarios/pmemobj/error_handling_tx_oom.cc engine_scenarios/pmemobj/mock_tx_alloc.cc LIBS json dl_libs) build_test_ext(NAME pmemobj_error_handling_tx_oid SRC_FILES engine_scenarios/pmemobj/error_handling_tx_oid.cc LIBS json libpmemobj_cpp) build_test_ext(NAME pmemobj_put_get_std_map_oid SRC_FILES engine_scenarios/pmemobj/put_get_std_map_oid.cc LIBS json libpmemobj_cpp) build_test(pmemobj_create_or_error_if_exists engine_scenarios/pmemobj/create_or_error_if_exists.cc) # Tests for memkind engines if (ENGINE_VCMAP OR ENGINE_VSMAP) build_test_ext(NAME memkind_error_handling SRC_FILES engine_scenarios/memkind/error_handling.cc LIBS json memkind) endif() # Tests for C API build_test(c_api_null_db_config c_api/null_db_config.c) # Tests for comparator build_test_ext(NAME comparator_basic_c SRC_FILES comparator/basic.c LIBS json) build_test_ext(NAME comparator_basic_persistent_c SRC_FILES comparator/basic_persistent.c LIBS json) build_test_ext(NAME comparator_custom_reopen_c SRC_FILES comparator/custom_reopen.c LIBS json) build_test_ext(NAME comparator_default_reopen_c SRC_FILES comparator/default_reopen.c LIBS json) build_test_ext(NAME comparator_custom_reopen_cpp SRC_FILES comparator/custom_reopen.cc LIBS json) build_test_ext(NAME comparator_default_reopen_cpp SRC_FILES comparator/default_reopen.cc LIBS json) # Tests for transaction build_test_ext(NAME transaction_put SRC_FILES engine_scenarios/transaction/put.cc LIBS json) build_test_ext(NAME transaction_remove SRC_FILES engine_scenarios/transaction/remove.cc LIBS json) build_test_ext(NAME transaction_put_pmreorder SRC_FILES engine_scenarios/transaction/put_pmreorder.cc LIBS json) build_test_ext(NAME transaction_not_supported SRC_FILES engine_scenarios/transaction/not_supported.cc LIBS json) # Tests for iterator build_test_ext(NAME iterator_basic SRC_FILES engine_scenarios/all/iterator_basic.cc LIBS json) build_test_ext(NAME iterator_sorted SRC_FILES engine_scenarios/sorted/iterator_sorted.cc LIBS json) build_test_ext(NAME iterator_not_supported SRC_FILES engine_scenarios/all/iterator_not_supported.cc LIBS json) ###################################### BLACKHOLE ############################## build_test(blackhole_test engines/blackhole/blackhole_test.cc) add_test_generic(NAME blackhole_test TRACERS none memcheck) # add_engine_test(ENGINE blackhole BINARY transaction_not_supported TRACERS none memcheck SCRIPT blackhole/default.cmake) ################################################################################ ##################################### CMAP #################################### if(ENGINE_CMAP) add_engine_test(ENGINE cmap BINARY c_api_null_db_config TRACERS none memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY open TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE cmap BINARY put_get_remove TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY put_get_remove_not_aligned TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY put_get_remove_charset_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE cmap BINARY put_get_remove_long_key TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY put_get_remove_params TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 4G PARAMS 400000) add_engine_test(ENGINE cmap BINARY put_get_remove_params TRACERS memcheck pmemcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE cmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) # XXX: https://github.com/pmem/libpmemobj-cpp/issues/516 # add_engine_test(ENGINE cmap # BINARY error_handling_oom # TRACERS none # SCRIPT pmemobj_based/default.cmake # DB_SIZE 50M) add_engine_test(ENGINE cmap BINARY iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_params TRACERS none memcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 8 50) endif() add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_gen_params TRACERS none memcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50 100) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_gen_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 8 50 100) endif() add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_single_op_params TRACERS none SCRIPT pmemobj_based/default.cmake PARAMS 1000) add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_single_op_params TRACERS memcheck SCRIPT pmemobj_based/default.cmake PARAMS 400) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE cmap BINARY concurrent_put_get_remove_single_op_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 250) endif() add_engine_test(ENGINE cmap BINARY persistent_put_get_std_map_multiple_reopen TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE cmap BINARY persistent_not_found_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE cmap BINARY persistent_overwrite_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE cmap BINARY persistent_put_remove_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE cmap BINARY persistent_put_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE cmap BINARY persistent_put_verify_asc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE cmap BINARY persistent_put_verify_desc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_tx_oom TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 200M) if(TESTS_LONG) add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_tx_oom TRACERS memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 200M) endif() add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_create TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake) add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_tx_oid TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake) add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_tx_path TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake) add_engine_test(ENGINE cmap BINARY pmemobj_error_handling_defrag TRACERS none memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY pmemobj_create_or_error_if_exists TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE cmap BINARY pmemobj_put_get_std_map_defrag TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE cmap BINARY pmemobj_put_get_std_map_oid TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 100 200) add_engine_test(ENGINE cmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 100 200) add_engine_test(ENGINE cmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 32 16) add_engine_test(ENGINE cmap BINARY iterator_basic TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE cmap BINARY iterator_concurrent TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8) add_engine_test(ENGINE cmap BINARY transaction_not_supported TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE cmap BINARY iterator_concurrent TRACERS helgrind drd SCRIPT pmemobj_based/default.cmake PARAMS 8) endif() endif(ENGINE_CMAP) ################################################################################ ###################################### CSMAP ################################### if(ENGINE_CSMAP) add_engine_test(ENGINE csmap BINARY c_api_null_db_config TRACERS none memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY open TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE csmap BINARY comparator_basic_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY comparator_basic_persistent_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY comparator_custom_reopen_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY comparator_default_reopen_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY comparator_custom_reopen_cpp TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY comparator_default_reopen_cpp TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY put_get_remove TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY put_get_remove_not_aligned TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY put_get_remove_charset_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE csmap BINARY put_get_remove_long_key TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY put_get_remove_params TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 4G PARAMS 400000) add_engine_test(ENGINE csmap BINARY put_get_remove_params TRACERS memcheck pmemcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE csmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) # XXX: investigate failure (possibly https://github.com/pmem/libpmemobj-cpp/issues/516) # add_engine_test(ENGINE csmap # BINARY error_handling_oom # TRACERS none # SCRIPT pmemobj_based/default.cmake # DB_SIZE 50M) add_engine_test(ENGINE csmap BINARY iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY sorted_iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY sorted_get_all_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS default 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS reverse 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_equal_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_below_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_equal_below_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE csmap BINARY sorted_get_between_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE csmap BINARY concurrent_iterate_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 24 200) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE csmap BINARY concurrent_iterate_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 4 50) endif() add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50) if(TESTS_PMEMOBJ_DRD_HELGRIND AND TESTS_LONG) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 8 10) if(TESTS_LONG) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 8 50) endif() endif() add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50 100) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_gen_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 8 50 100) endif() add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_single_op_params TRACERS none SCRIPT pmemobj_based/default.cmake PARAMS 1000) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_single_op_params TRACERS memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 400) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_single_op_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 100) add_engine_test(ENGINE csmap BINARY iterator_concurrent TRACERS drd # helgrind XXX: https://github.com/pmem/libpmemobj-cpp/issues/797 SCRIPT pmemobj_based/default.cmake PARAMS 8) if(TESTS_LONG) add_engine_test(ENGINE csmap BINARY concurrent_put_get_remove_single_op_params TRACERS drd # helgrind XXX: skip list lock order error SCRIPT pmemobj_based/default.cmake PARAMS 400) endif() endif() add_engine_test(ENGINE csmap BINARY persistent_put_get_std_map_multiple_reopen TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE csmap BINARY persistent_not_found_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY persistent_overwrite_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY persistent_put_remove_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY persistent_put_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE csmap BINARY persistent_put_verify_asc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE csmap BINARY persistent_put_verify_desc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE csmap BINARY pmemobj_error_handling_tx_oom TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 200M) if(TESTS_LONG) add_engine_test(ENGINE csmap BINARY pmemobj_error_handling_tx_oom TRACERS memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 200M) endif() add_engine_test(ENGINE csmap BINARY pmemobj_error_handling_create TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake) add_engine_test(ENGINE csmap BINARY pmemobj_error_handling_tx_oid TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake) add_engine_test(ENGINE csmap BINARY pmemobj_error_handling_tx_path TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake) add_engine_test(ENGINE csmap BINARY pmemobj_create_or_error_if_exists TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) # XXX - CSMAP does not support defrag yet # add_engine_test(ENGINE csmap # BINARY pmemobj_error_handling_defrag # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake) # XXX - CSMAP does not support defrag yet # add_engine_test(ENGINE csmap # BINARY pmemobj_put_get_std_map_defrag # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 1000 100 200) add_engine_test(ENGINE csmap BINARY pmemobj_put_get_std_map_oid TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 100 200) add_engine_test(ENGINE csmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 100 200) add_engine_test(ENGINE csmap BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 32 16) add_engine_test(ENGINE csmap BINARY iterator_basic TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE csmap BINARY iterator_sorted TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS false) add_engine_test(ENGINE csmap BINARY iterator_concurrent TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 true) add_engine_test(ENGINE csmap BINARY transaction_not_supported TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) endif(ENGINE_CSMAP) ################################################################################ ###################################### VCMAP ################################### if(ENGINE_VCMAP) add_engine_test(ENGINE vcmap BINARY c_api_null_db_config TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY open TRACERS none memcheck SCRIPT memkind_based/default_no_config.cmake) add_engine_test(ENGINE vcmap BINARY put_get_remove TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY put_get_remove_not_aligned TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY put_get_remove_charset_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE vcmap BINARY put_get_remove_long_key TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY put_get_remove_params TRACERS none SCRIPT memkind_based/default.cmake DB_SIZE 4294967296 PARAMS 400000) add_engine_test(ENGINE vcmap BINARY put_get_remove_params TRACERS memcheck SCRIPT memkind_based/default.cmake DB_SIZE 4294967296 PARAMS 4000) add_engine_test(ENGINE vcmap BINARY put_get_std_map TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 1000 100 200) # XXX: https://github.com/pmem/pmemkv/issues/623 # add_engine_test(ENGINE vcmap # BINARY error_handling_oom # TRACERS none memcheck # SCRIPT memkind_based/default.cmake # DB_SIZE 50485760) add_engine_test(ENGINE vcmap BINARY iterate TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY concurrent_put_get_remove_params TRACERS none memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT memkind_based/default.cmake PARAMS 8 50) add_engine_test(ENGINE vcmap BINARY concurrent_put_get_remove_gen_params TRACERS none memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT memkind_based/default.cmake PARAMS 8 50 100) add_engine_test(ENGINE vcmap BINARY concurrent_put_get_remove_single_op_params TRACERS none # XXX - tbb lock does not work well with drd or helgrind SCRIPT memkind_based/default.cmake DB_SIZE "MIN_JEMALLOC_ARENA_SIZE" PARAMS 1000) add_engine_test(ENGINE vcmap BINARY concurrent_put_get_remove_single_op_params TRACERS memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT memkind_based/default.cmake DB_SIZE "MIN_JEMALLOC_ARENA_SIZE" PARAMS 400) add_engine_test(ENGINE vcmap BINARY memkind_error_handling TRACERS none memcheck SCRIPT memkind_based/memkind/error_handling.cmake) add_engine_test(ENGINE vcmap BINARY iterator_basic TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vcmap BINARY iterator_concurrent TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 8) add_engine_test(ENGINE vcmap BINARY transaction_not_supported TRACERS none memcheck SCRIPT memkind_based/default.cmake) endif(ENGINE_VCMAP) ################################################################################ ###################################### VSMAP ################################### if(ENGINE_VSMAP) add_engine_test(ENGINE vsmap BINARY c_api_null_db_config TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY open TRACERS none memcheck SCRIPT memkind_based/default_no_config.cmake) add_engine_test(ENGINE vsmap BINARY comparator_basic_c TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY put_get_remove TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY put_get_remove_not_aligned TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY put_get_remove_charset_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE vsmap BINARY put_get_remove_long_key TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY put_get_remove_params TRACERS none SCRIPT memkind_based/default.cmake DB_SIZE 4294967296 PARAMS 400000) add_engine_test(ENGINE vsmap BINARY put_get_remove_params TRACERS memcheck SCRIPT memkind_based/default.cmake DB_SIZE 4294967296 PARAMS 4000) add_engine_test(ENGINE vsmap BINARY put_get_std_map TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE vsmap BINARY error_handling_oom TRACERS none memcheck SCRIPT memkind_based/default.cmake DB_SIZE 50485760) add_engine_test(ENGINE vsmap BINARY iterate TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY sorted_iterate TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY sorted_get_all_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_above_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS default 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_above_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS reverse 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_equal_above_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_below_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_equal_below_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE vsmap BINARY sorted_get_between_gen_params TRACERS none memcheck SCRIPT memkind_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE vsmap BINARY memkind_error_handling TRACERS none memcheck SCRIPT memkind_based/memkind/error_handling.cmake) add_engine_test(ENGINE vsmap BINARY iterator_basic TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY iterator_sorted TRACERS none memcheck SCRIPT memkind_based/default.cmake) add_engine_test(ENGINE vsmap BINARY transaction_not_supported TRACERS none memcheck SCRIPT memkind_based/default.cmake) endif(ENGINE_VSMAP) ################################################################################ ###################################### TREE3 ################################### if(ENGINE_TREE3) # XXX - all memcheck and pmemcheck tests are disabled due to failures # Need to investigate add_engine_test(ENGINE tree3 BINARY c_api_null_db_config TRACERS none #memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY open TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) # XXX - add comparator support to tree3 # add_engine_test(ENGINE tree3 # BINARY c_api_comparator # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY put_get_remove TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY put_get_remove_not_aligned TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY put_get_remove_charset_params TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE tree3 BINARY put_get_remove_long_key TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY put_get_remove_params TRACERS none #memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 4G PARAMS 400000) add_engine_test(ENGINE tree3 BINARY put_get_std_map TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE tree3 BINARY error_handling_oom TRACERS none #memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 20M) # XXX - inverstigate failure # add_engine_test(ENGINE tree3 # BINARY iterate # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY persistent_put_get_std_map_multiple_reopen TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE tree3 BINARY persistent_not_found_verify TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE tree3 BINARY persistent_overwrite_verify TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE tree3 BINARY persistent_put_remove_verify TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE tree3 BINARY persistent_put_verify TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE tree3 BINARY persistent_put_verify_asc_params TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 6000) add_engine_test(ENGINE tree3 BINARY persistent_put_verify_asc_params TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 144) # 144 is limit for one inner node add_engine_test(ENGINE tree3 BINARY persistent_put_verify_desc_params TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 6000) add_engine_test(ENGINE tree3 BINARY persistent_put_verify_desc_params TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 144) # 144 is limit for one inner node add_engine_test(ENGINE tree3 BINARY pmemobj_error_handling_tx_oom TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY pmemobj_error_handling_create TRACERS none #memcheck SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake) add_engine_test(ENGINE tree3 BINARY pmemobj_create_or_error_if_exists TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) # XXX - defrag not supported # add_engine_test(ENGINE tree3 # BINARY pmemobj_error_handling_tx_oid # TRACERS none memcheck # SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake) # XXX - defrag not supported # add_engine_test(ENGINE tree3 # BINARY pmemobj_error_handling_tx_path # TRACERS none memcheck # SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake) # XXX - defrag not supported # add_engine_test(ENGINE tree3 # BINARY pmemobj_error_handling_defrag # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake) # XXX - defrag not supported # add_engine_test(ENGINE tree3 # BINARY pmemobj_put_get_std_map_defrag # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 1000 100 200) add_engine_test(ENGINE tree3 BINARY pmemobj_put_get_std_map_oid TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 100 200) add_engine_test(ENGINE tree3 BINARY put_get_std_map TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 100 200) add_engine_test(ENGINE tree3 BINARY put_get_std_map TRACERS none #memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 32 16) # XXX fail to investigate # add_engine_test(ENGINE tree3 # BINARY sorted_iterate # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_all_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_above_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_equal_above_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_below_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_equal_below_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) # XXX to be tested # add_engine_test(ENGINE tree3 # BINARY sorted_get_between_gen_params # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 32 8) add_engine_test(ENGINE tree3 BINARY transaction_not_supported TRACERS none memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE tree3 BINARY iterator_not_supported TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) endif(ENGINE_TREE3) ################################################################################ ###################################### STREE ################################### if(ENGINE_STREE) add_engine_test(ENGINE stree BINARY c_api_null_db_config TRACERS none memcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY open TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE stree BINARY comparator_basic_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY comparator_basic_persistent_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY comparator_custom_reopen_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY comparator_default_reopen_c TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY comparator_custom_reopen_cpp TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY comparator_default_reopen_cpp TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY put_get_remove TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY put_get_remove_not_aligned TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY put_get_remove_charset_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE stree BINARY put_get_remove_long_key TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY put_get_remove_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 10000) if (TESTS_LONG) add_engine_test(ENGINE stree BINARY put_get_remove_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 4G PARAMS 400000) endif() if(PMREORDER_SUPPORTED) add_engine_test(ENGINE stree BINARY pmreorder_insert TRACERS none SCRIPT pmemobj_based/pmreorder/insert.cmake) add_engine_test(ENGINE stree BINARY pmreorder_erase TRACERS none SCRIPT pmemobj_based/pmreorder/erase.cmake) add_engine_test(ENGINE stree BINARY pmreorder_iterator TRACERS none SCRIPT pmemobj_based/pmreorder/iterator.cmake) add_engine_test(ENGINE stree BINARY pmreorder_recover TRACERS none SCRIPT pmemobj_based/pmreorder/recover.cmake) endif() add_engine_test(ENGINE stree BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 20 200) # XXX: investigate failure (possibly https://github.com/pmem/libpmemobj-cpp/issues/516) # add_engine_test(ENGINE stree # BINARY error_handling_oom # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake # DB_SIZE 20M) add_engine_test(ENGINE stree BINARY iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY persistent_put_get_std_map_multiple_reopen TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 20 200) add_engine_test(ENGINE stree BINARY persistent_not_found_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY persistent_overwrite_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY persistent_put_remove_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY persistent_put_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE stree BINARY persistent_put_verify_asc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE stree BINARY persistent_put_verify_desc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE stree BINARY pmemobj_error_handling_create TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake) add_engine_test(ENGINE stree BINARY pmemobj_create_or_error_if_exists TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) # XXX - defrag not supported # add_engine_test(ENGINE stree # BINARY pmemobj_error_handling_tx_oid # TRACERS none memcheck # SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake) # XXX - defrag not supported # add_engine_test(ENGINE stree # BINARY pmemobj_error_handling_tx_path # TRACERS none memcheck # SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake) # XXX - defrag not supported # add_engine_test(ENGINE stree # BINARY pmemobj_error_handling_defrag # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake) # XXX - defrag not supported # add_engine_test(ENGINE stree # BINARY pmemobj_put_get_std_map_defrag # TRACERS none memcheck pmemcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 1000 100 200) # XXX: investigate failure (possibly https://github.com/pmem/libpmemobj-cpp/issues/516) # add_engine_test(ENGINE stree # BINARY pmemobj_error_handling_tx_oom # TRACERS none # SCRIPT pmemobj_based/default.cmake # DB_SIZE 200M) # if(TESTS_LONG) # add_engine_test(ENGINE stree # BINARY pmemobj_error_handling_tx_oom # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # DB_SIZE 200M) # endif() add_engine_test(ENGINE stree BINARY pmemobj_put_get_std_map_oid TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 20 200) add_engine_test(ENGINE stree BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 20 200) add_engine_test(ENGINE stree BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 32 16) add_engine_test(ENGINE stree BINARY sorted_iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY sorted_get_all_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE stree BINARY sorted_get_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS default 32 8) add_engine_test(ENGINE stree BINARY sorted_get_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS reverse 32 8) add_engine_test(ENGINE stree BINARY sorted_get_equal_above_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE stree BINARY sorted_get_below_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE stree BINARY sorted_get_equal_below_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE stree BINARY sorted_get_between_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 32 8) add_engine_test(ENGINE stree BINARY iterator_basic TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY iterator_sorted TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE stree BINARY transaction_not_supported TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) endif(ENGINE_STREE) ################################################################################ ###################################### RADIX ################################### if(ENGINE_RADIX) set(DRAM_CACHING_OPTS 0 1) foreach(dram_caching ${DRAM_CACHING_OPTS}) if(dram_caching EQUAL 1) set(EXTRA_CFG_PARAM {"dram_caching":1,"cache_size":100,"log_size":50000}) set(PMEMCHECK "") set(MEMCHECK "") set(MEMCHECK_NO_CACHE "") else() set(EXTRA_CFG_PARAM {"dram_caching":0}) set(PMEMCHECK "pmemcheck") set(MEMCHECK "memcheck") set(MEMCHECK_NO_CACHE "memcheck") endif() # XXX: add drd/helgrind/pmemcheck tests for radix with dram_caching==1 add_engine_test(ENGINE radix BINARY c_api_null_db_config TRACERS none ${MEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY open TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default_no_config.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove_not_aligned TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 1000 8 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # XXX: https://github.com/pmem/pmemkv/issues/1014 if(dram_caching EQUAL 0) add_engine_test(ENGINE radix BINARY error_handling_oom TRACERS none memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 20M EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) endif() add_engine_test(ENGINE radix BINARY iterate TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_get_std_map_multiple_reopen TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 1000 8 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_not_found_verify TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_overwrite_verify TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_remove_verify TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_verify TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_verify_asc_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 6000 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_verify_desc_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 6000 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) if(dram_caching EQUAL 0) # tx_oom does not work with DRAM layer since we rely on non-transactional put add_engine_test(ENGINE radix BINARY pmemobj_error_handling_tx_oom TRACERS none SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) if(TESTS_LONG) add_engine_test(ENGINE radix BINARY pmemobj_error_handling_tx_oom TRACERS memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 200M EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) endif() endif() add_engine_test(ENGINE radix BINARY pmemobj_error_handling_create TRACERS none ${MEMCHECK} SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmemobj_error_handling_tx_oid TRACERS none ${MEMCHECK} SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmemobj_error_handling_tx_path TRACERS none ${MEMCHECK} SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmemobj_create_or_error_if_exists TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default_no_config.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # XXX - defrag not supported # add_engine_test(ENGINE radix # BINARY pmemobj_error_handling_defrag # TRACERS none ${MEMCHECK} # SCRIPT pmemobj_based/default.cmake) # XXX - defrag not supported # add_engine_test(ENGINE radix # BINARY pmemobj_put_get_std_map_defrag # TRACERS none ${MEMCHECK} ${PMEMCHECK} # SCRIPT pmemobj_based/default.cmake # PARAMS 1000 100 200) add_engine_test(ENGINE radix BINARY pmemobj_put_get_std_map_oid TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 8 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 8 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 32 16 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # small keys add_engine_test(ENGINE radix BINARY put_get_remove_charset_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 1000 16 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # bigger keys add_engine_test(ENGINE radix BINARY put_get_remove_charset_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 1000 1024 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove_long_key TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove_params TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 400000 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove_params TRACERS ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 4000 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 1000 100 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_iterate TRACERS none ${MEMCHECK} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_all_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_above_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS default 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_equal_above_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_below_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_equal_below_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY sorted_get_between_gen_params TRACERS none ${MEMCHECK_NO_CACHE} ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake PARAMS 32 8 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) if(dram_caching EQUAL 0) add_engine_test(ENGINE radix BINARY transaction_put TRACERS none memcheck ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY transaction_remove TRACERS none memcheck ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY iterator_basic TRACERS none memcheck ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY iterator_sorted TRACERS none memcheck ${PMEMCHECK} SCRIPT pmemobj_based/default.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) endif() # XXX: optimize those time execution for dram_caching == 1 if(PMREORDER_SUPPORTED AND dram_caching EQUAL 0) add_engine_test(ENGINE radix BINARY transaction_put_pmreorder TRACERS none SCRIPT pmemobj_based/pmreorder/insert.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmreorder_insert TRACERS none SCRIPT pmemobj_based/pmreorder/insert.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmreorder_erase TRACERS none SCRIPT pmemobj_based/pmreorder/erase.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmreorder_iterator TRACERS none SCRIPT pmemobj_based/pmreorder/iterator.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY pmreorder_recover TRACERS none SCRIPT pmemobj_based/pmreorder/recover.cmake EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) endif() # Smaller params for memcheck tests with cache if(TESTS_LONG AND dram_caching EQUAL 1) # XXX: it also requires optimization - few tests timeout on pmem # add_engine_test(ENGINE radix # BINARY put_get_remove_charset_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 25 50 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS memcheck SCRIPT pmemobj_based/default.cmake PARAMS 100 8 200 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_std_map TRACERS memcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 100 8 20 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY put_get_remove_params TRACERS memcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 100 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_verify_asc_params TRACERS memcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 600 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) add_engine_test(ENGINE radix BINARY persistent_put_verify_desc_params TRACERS memcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 600 EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_all_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_above_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS default 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_equal_above_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_below_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_equal_below_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) # add_engine_test(ENGINE radix # BINARY sorted_get_between_gen_params # TRACERS memcheck # SCRIPT pmemobj_based/default.cmake # PARAMS 10 4 # EXTRA_CONFIG_PARAMS ${EXTRA_CFG_PARAM}) endif() endforeach() endif(ENGINE_RADIX) ################################################################################ #################################### ROBINHOOD ################################# if (ENGINE_ROBINHOOD) # XXX: https://github.com/pmem/pmemkv/issues/916 # add_engine_test(ENGINE robinhood # BINARY error_handling_oom # TRACERS none memcheck # SCRIPT pmemobj_based/default.cmake # DB_SIZE 20M) add_engine_test(ENGINE robinhood BINARY iterator_not_supported TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE robinhood BINARY open TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE robinhood BINARY put_get_remove_charset_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 16 8) add_engine_test(ENGINE robinhood BINARY put_get_remove_params TRACERS none SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 40000) add_engine_test(ENGINE robinhood BINARY put_get_remove_params TRACERS memcheck pmemcheck SCRIPT pmemobj_based/default.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE robinhood BINARY put_get_remove TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE robinhood BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 8 8) add_engine_test(ENGINE robinhood BINARY iterate TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_single_op_params TRACERS none SCRIPT pmemobj_based/default.cmake PARAMS 1000) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_single_op_params TRACERS memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 400) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_gen_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 8 50 8) add_engine_test(ENGINE robinhood BINARY concurrent_iterate_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 24 200 0) add_engine_test(ENGINE robinhood BINARY persistent_not_found_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE robinhood BINARY persistent_overwrite_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE robinhood BINARY persistent_put_get_std_map_multiple_reopen TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default.cmake PARAMS 1000 8 8) add_engine_test(ENGINE robinhood BINARY persistent_put_remove_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) add_engine_test(ENGINE robinhood BINARY persistent_put_verify_asc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE robinhood BINARY persistent_put_verify_desc_params TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake DB_SIZE 1G PARAMS 4000) add_engine_test(ENGINE robinhood BINARY persistent_put_verify TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/persistent/insert_check.cmake) if(TESTS_PMEMOBJ_DRD_HELGRIND) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_single_op_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 250) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 8 50) add_engine_test(ENGINE robinhood BINARY concurrent_put_get_remove_gen_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 8 50 8) add_engine_test(ENGINE robinhood BINARY concurrent_iterate_params TRACERS drd helgrind SCRIPT pmemobj_based/default.cmake PARAMS 4 50 0) endif() add_engine_test(ENGINE robinhood BINARY pmemobj_error_handling_create TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_create.cmake) add_engine_test(ENGINE robinhood BINARY pmemobj_error_handling_tx_oid TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_oid.cmake) add_engine_test(ENGINE robinhood BINARY pmemobj_error_handling_tx_path TRACERS none memcheck SCRIPT pmemobj_based/pmemobj/error_handling_tx_path.cmake) add_engine_test(ENGINE robinhood BINARY pmemobj_create_or_error_if_exists TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/default_no_config.cmake) add_engine_test(ENGINE robinhood BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake PARAMS 1000 8 8) add_engine_test(ENGINE robinhood BINARY put_get_std_map TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/create_if_missing.cmake PARAMS 128 8 8) add_engine_test(ENGINE robinhood BINARY pmemobj_put_get_std_map_oid TRACERS none memcheck pmemcheck SCRIPT pmemobj_based/pmemobj/put_get_std_map_oid.cmake PARAMS 1000 8 8) if(PMREORDER_SUPPORTED) add_engine_test(ENGINE robinhood BINARY pmreorder_insert TRACERS none SCRIPT pmemobj_based/pmreorder/insert.cmake) add_engine_test(ENGINE robinhood BINARY pmreorder_erase TRACERS none SCRIPT pmemobj_based/pmreorder/erase.cmake) if (TESTS_LONG) add_engine_test(ENGINE robinhood BINARY pmreorder_recover TRACERS none SCRIPT pmemobj_based/pmreorder/recover.cmake) endif() endif() endif() ################################################################################ ###################################### DRAM_VCMAP ################################### if(ENGINE_DRAM_VCMAP) add_engine_test(ENGINE dram_vcmap BINARY c_api_null_db_config TRACERS none memcheck SCRIPT dram/default.cmake) add_engine_test(ENGINE dram_vcmap BINARY put_get_remove TRACERS none memcheck SCRIPT dram/default.cmake) add_engine_test(ENGINE dram_vcmap BINARY put_get_remove_charset_params TRACERS none memcheck SCRIPT dram/default.cmake PARAMS 16 8) add_engine_test(ENGINE dram_vcmap BINARY put_get_remove_long_key TRACERS none memcheck SCRIPT dram/default.cmake) add_engine_test(ENGINE dram_vcmap BINARY put_get_remove_params TRACERS none SCRIPT dram/default.cmake PARAMS 400000) add_engine_test(ENGINE dram_vcmap BINARY put_get_remove_params TRACERS memcheck SCRIPT dram/default.cmake PARAMS 4000) add_engine_test(ENGINE dram_vcmap BINARY put_get_std_map TRACERS none memcheck SCRIPT dram/default.cmake PARAMS 1000 100 200) add_engine_test(ENGINE dram_vcmap BINARY iterate TRACERS none memcheck SCRIPT dram/default.cmake) add_engine_test(ENGINE dram_vcmap BINARY concurrent_put_get_remove_params TRACERS none memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT dram/default.cmake PARAMS 8 50) add_engine_test(ENGINE dram_vcmap BINARY concurrent_put_get_remove_gen_params TRACERS none memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT dram/default.cmake PARAMS 8 50 100) add_engine_test(ENGINE dram_vcmap BINARY concurrent_put_get_remove_single_op_params TRACERS none # XXX - tbb lock does not work well with drd or helgrind SCRIPT dram/default.cmake PARAMS 1000) add_engine_test(ENGINE dram_vcmap BINARY concurrent_put_get_remove_single_op_params TRACERS memcheck # XXX - tbb lock does not work well with drd or helgrind SCRIPT dram/default.cmake PARAMS 400) add_engine_test(ENGINE dram_vcmap BINARY iterator_basic TRACERS none memcheck SCRIPT dram/default.cmake) add_engine_test(ENGINE dram_vcmap BINARY iterator_concurrent TRACERS none memcheck SCRIPT dram/default.cmake PARAMS 8) add_engine_test(ENGINE dram_vcmap BINARY transaction_not_supported TRACERS none memcheck SCRIPT dram/default.cmake) endif(ENGINE_DRAM_VCMAP) ################################################################################ pmemkv-1.5.0/tests/README.md000066400000000000000000000024751410000423300154300ustar00rootroot00000000000000# Content This directory contains tests for pmemkv. They are grouped into directories: - **c_api** - most of pmemkv's tests use C++ API, these tests use only C API - **compatibility** - compatibility checks between different pmemkv versions - **config** - tests for all functions in config's API - **engine_scenarios** - test scenarios parameterized with at least engine name; they are grouped into sub-sections related to specific group of engines' capabilities - **engines** - tests and scripts related to pmemkv's engines - **engines-experimental** - tests and scripts related to experimental engines - and additional test(s) in main directory # Tests execution Before executing tests it's required to build pmemkv's sources and tests. See [INSTALLING.md](../INSTALLING.md) for details. There are CMake's options related to tests - see all options in top-level CMakeLists.txt with prefix `TESTS_`. One of them - `TESTS_USE_FORCED_PMEM=ON` speeds up tests execution on emulated pmem. It's useful for long (e.g. concurrent) tests. To run all tests execute: ```sh make test ``` or, if you want to see an output/logs of failed tests, run: ```sh ctest --output-on-failure ``` There are other parameters to use in `ctest` command. To see the full list read [ctest(1) manpage](https://cmake.org/cmake/help/latest/manual/ctest.1.html). pmemkv-1.5.0/tests/c_api/000077500000000000000000000000001410000423300152145ustar00rootroot00000000000000pmemkv-1.5.0/tests/c_api/null_db_config.c000066400000000000000000000131741410000423300203320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include #include "unittest.h" #include /** * Passing null as 'db' or 'config' in C API produces INVALID_ARGUMENT status */ void null_db_all_funcs_test() { /** * TEST: null passed as db to pmemkv_* functions */ 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; UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_count_above(NULL, key1, strlen(key1), &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_count_equal_above(NULL, key1, strlen(key1), &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_count_below(NULL, key1, strlen(key1), &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_count_equal_below(NULL, key1, strlen(key1), &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_count_between(NULL, key1, strlen(key1), key2, strlen(key2), &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_all(NULL, NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_above(NULL, key1, strlen(key1), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_equal_above(NULL, key1, strlen(key1), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_below(NULL, key1, strlen(key1), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_equal_below(NULL, key1, strlen(key1), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_between(NULL, key1, strlen(key1), key2, strlen(key2), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_exists(NULL, key1, strlen(key1)); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get(NULL, key1, strlen(key1), NULL, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_get_copy(NULL, key1, strlen(key1), val, 10, &cnt); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_put(NULL, key1, strlen(key1), value1, strlen(value1)); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_remove(NULL, key1, strlen(key1)); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_defrag(NULL, 0, 100); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); pmemkv_tx *tx; s = pmemkv_tx_begin(NULL, &tx); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_tx_begin((pmemkv_db *)0x1, NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); } void null_config_test(const char *engine) { /** * TEST: null passed as config to pmemkv_open() */ pmemkv_config *empty_cfg = NULL; pmemkv_db *db = NULL; int s = pmemkv_open(engine, empty_cfg, &db); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); } void null_db_test(const char *engine) { /** * TEST: null passed as db to pmemkv_open() */ pmemkv_config *cfg = pmemkv_config_new(); UT_ASSERTne(cfg, NULL); int s = pmemkv_open(engine, cfg, NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); /* Config should be deleted by pmemkv */ } void null_tx_test(const char *engine) { const char *key1 = "key1"; int s = pmemkv_tx_put(NULL, key1, strlen(key1), key1, strlen(key1)); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_tx_remove(NULL, key1, strlen(key1)); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_tx_commit(NULL); UT_ASSERT(s == PMEMKV_STATUS_INVALID_ARGUMENT); pmemkv_tx_end(NULL); } void null_iterator_all_funcs_test() { /** * TEST: null passed as iterator to pmemkv_* functions */ pmemkv_iterator *it1; pmemkv_write_iterator *it2; size_t cnt; const char *key1 = "key1"; const char *val1; char *val2; int s = pmemkv_iterator_new(NULL, &it1); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_write_iterator_new(NULL, &it2); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek(NULL, key1, strlen(key1)); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_lower(NULL, key1, strlen(key1)); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_lower_eq(NULL, key1, strlen(key1)); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_higher(NULL, key1, strlen(key1)); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_higher_eq(NULL, key1, strlen(key1)); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_to_first(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_seek_to_last(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_is_next(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_next(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_prev(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_key(NULL, &val1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_iterator_read_range(NULL, 0, 10, &val1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_write_iterator_write_range(NULL, 0, 10, &val2, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); s = pmemkv_write_iterator_commit(NULL); UT_ASSERTeq(s, PMEMKV_STATUS_INVALID_ARGUMENT); /* returns void */ pmemkv_write_iterator_abort(NULL); /* returns void */ pmemkv_iterator_delete(NULL); /* returns void */ pmemkv_write_iterator_delete(NULL); } int main(int argc, char *argv[]) { START(); if (argc < 2) UT_FATAL("usage %s: engine", argv[0]); null_db_all_funcs_test(); null_config_test(argv[1]); null_db_test(argv[1]); null_iterator_all_funcs_test(); return 0; } pmemkv-1.5.0/tests/cmake/000077500000000000000000000000001410000423300152215ustar00rootroot00000000000000pmemkv-1.5.0/tests/cmake/pmreorder.conf000066400000000000000000000000501410000423300200620ustar00rootroot00000000000000{ "no_reorder" : "NoReorderNoCheck" } pmemkv-1.5.0/tests/cmake/run_default.cmake000066400000000000000000000002731410000423300205350ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() execute(${TEST_EXECUTABLE} ${DIR}/testfile) finish() pmemkv-1.5.0/tests/common/000077500000000000000000000000001410000423300154315ustar00rootroot00000000000000pmemkv-1.5.0/tests/common/check_is_pmem.cpp000066400000000000000000000016501410000423300207250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2020, Intel Corporation */ /* * check_is_pmem.cpp -- check if path points to a directory on pmem. */ #include #include #include /* * return value is: * - 0 when path points to pmem * - 1 when path points to non-pmem * - 2 when error occurred */ int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << "usage: " << argv[0] << " filepath\n"; return 2; } auto path = argv[1]; int is_pmem; size_t size; int flags = PMEM_FILE_CREATE; #ifdef _WIN32 void *addr = pmem_map_fileU(path, 4096, flags, 0, &size, &is_pmem); #else void *addr = pmem_map_file(path, 4096, flags, 0, &size, &is_pmem); #endif if (addr == nullptr) { perror("pmem_map_file failed"); return 2; } pmem_unmap(addr, size); if (remove(path) != 0) { perror("remove(path) failed"); return 2; } if (is_pmem) return 0; else return 1; } pmemkv-1.5.0/tests/common/test_backtrace.c000066400000000000000000000074361410000423300205650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2015-2020, Intel Corporation */ /* * backtrace.c -- backtrace reporting routines */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "test_backtrace.h" #include #include #include #include #include #ifdef USE_LIBUNWIND #define UNW_LOCAL_ONLY #include #include #define PROCNAMELEN 256 /* * test_dump_backtrace -- dump stacktrace to error log using libunwind */ void test_dump_backtrace(void) { unw_context_t context; unw_proc_info_t pip; pip.unwind_info = NULL; int ret = unw_getcontext(&context); if (ret) { fprintf(stderr, "unw_getcontext: %s [%d]\n", unw_strerror(ret), ret); return; } unw_cursor_t cursor; ret = unw_init_local(&cursor, &context); if (ret) { fprintf(stderr, "unw_init_local: %s [%d]\n", unw_strerror(ret), ret); return; } ret = unw_step(&cursor); char procname[PROCNAMELEN]; unsigned i = 0; while (ret > 0) { ret = unw_get_proc_info(&cursor, &pip); if (ret) { fprintf(stderr, "[%u] unw_get_proc_info: %s [%d]\n", i, unw_strerror(ret), ret); break; } unw_word_t off; ret = unw_get_proc_name(&cursor, procname, PROCNAMELEN, &off); if (ret && ret != -UNW_ENOMEM) { if (ret != -UNW_EUNSPEC) { fprintf(stderr, "[%u] unw_get_proc_name: %s [%d]\n", i, unw_strerror(ret), ret); } strcpy(procname, "?"); } void *ptr = (void *)(pip.start_ip + off); Dl_info dlinfo; const char *fname = "?"; if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname) fname = dlinfo.dli_fname; printf("%u: %s (%s%s+0x%lx) [%p]\n", i++, fname, procname, ret == -UNW_ENOMEM ? "..." : "", off, ptr); ret = unw_step(&cursor); if (ret < 0) fprintf(stderr, "unw_step: %s [%d]\n", unw_strerror(ret), ret); } } #else /* USE_LIBUNWIND */ #define BSIZE 100 #ifndef _WIN32 #include /* * test_dump_backtrace -- dump stacktrace to error log using libc's backtrace */ void test_dump_backtrace(void) { int j, nptrs; void *buffer[BSIZE]; char **strings; nptrs = backtrace(buffer, BSIZE); strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { fprintf(stderr, "backtrace_symbols %s\n", strerror(errno)); return; } for (j = 0; j < nptrs; j++) printf("%u: %s\n", j, strings[j]); free(strings); } #else /* _WIN32 */ #include #include #include #include /* * test_dump_backtrace -- dump stacktrace to error log */ void test_dump_backtrace(void) { void *buffer[BSIZE]; unsigned nptrs; SYMBOL_INFO *symbol; HANDLE proc_hndl = GetCurrentProcess(); SymInitialize(proc_hndl, NULL, TRUE); nptrs = CaptureStackBackTrace(0, BSIZE, buffer, NULL); symbol = calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR), 1); symbol->MaxNameLen = MAX_SYM_NAME - 1; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); for (unsigned i = 0; i < nptrs; i++) { if (SymFromAddr(proc_hndl, (DWORD64)buffer[i], 0, symbol)) { printf("%u: %s [%p]\n", nptrs - i - 1, symbol->Name, buffer[i]); } else { printf("%u: [%p]\n", nptrs - i - 1, buffer[i]); } } free(symbol); } #endif /* _WIN32 */ #endif /* USE_LIBUNWIND */ /* * test_sighandler -- fatal signal handler */ void test_sighandler(int sig) { printf("\nSignal: %s, backtrace:\n", strsignal(sig)); test_dump_backtrace(); printf("\n"); exit(128 + sig); } /* * test_register_sighandlers -- register signal handlers for various fatal * signals */ void test_register_sighandlers(void) { signal(SIGSEGV, test_sighandler); signal(SIGABRT, test_sighandler); signal(SIGILL, test_sighandler); signal(SIGFPE, test_sighandler); signal(SIGINT, test_sighandler); #ifndef _WIN32 signal(SIGALRM, test_sighandler); signal(SIGQUIT, test_sighandler); signal(SIGBUS, test_sighandler); #endif } pmemkv-1.5.0/tests/common/test_backtrace.h000066400000000000000000000004731410000423300205640ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2015-2020, Intel Corporation */ #ifndef TEST_BACKTRACE_H #define TEST_BACKTRACE_H #ifdef __cplusplus extern "C" { #endif void test_dump_backtrace(void); void test_sighandler(int sig); void test_register_sighandlers(void); #ifdef __cplusplus } #endif #endif pmemkv-1.5.0/tests/common/unittest.h000066400000000000000000000057401410000423300174670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #ifndef PMEMKV_UNITTEST_H #define PMEMKV_UNITTEST_H #ifdef __cplusplus extern "C" { #endif #include "test_backtrace.h" #include #include #include #include #include #define START() test_register_sighandlers() static inline void UT_OUT(const char *format, ...) { va_list args_list; va_start(args_list, format); vfprintf(stdout, format, args_list); va_end(args_list); fprintf(stdout, "\n"); } static inline void UT_FATAL(const char *format, ...) { va_list args_list; va_start(args_list, format); vfprintf(stderr, format, args_list); va_end(args_list); fprintf(stderr, "\n"); abort(); } /* assert a condition is true at runtime */ #define UT_ASSERT(cnd) \ ((void)((cnd) || \ (UT_FATAL("%s:%d %s - assertion failure: %s, errormsg: %s", __FILE__, \ __LINE__, __func__, #cnd, pmemkv_errormsg()), \ 0))) /* assertion with extra info printed if assertion fails at runtime */ #define UT_ASSERTinfo(cnd, info) \ ((void)((cnd) || \ (UT_FATAL("%s:%d %s - assertion failure: %s (%s = %s), errormsg: %s", \ __FILE__, __LINE__, __func__, #cnd, #info, info, \ pmemkv_errormsg()), \ 0))) /* assert two integer values are equal at runtime */ #define UT_ASSERTeq(lhs, rhs) \ ((void)(((lhs) == (rhs)) || \ (UT_FATAL("%s:%d %s - assertion failure: %s (0x%llx) == %s " \ "(0x%llx), errormsg: %s", \ __FILE__, __LINE__, __func__, #lhs, (unsigned long long)(lhs), \ #rhs, (unsigned long long)(rhs), pmemkv_errormsg()), \ 0))) /* assert two integer values are not equal at runtime */ #define UT_ASSERTne(lhs, rhs) \ ((void)(((lhs) != (rhs)) || \ (UT_FATAL("%s:%d %s - assertion failure: %s (0x%llx) != %s " \ "(0x%llx), errormsg: %s", \ __FILE__, __LINE__, __func__, #lhs, (unsigned long long)(lhs), \ #rhs, (unsigned long long)(rhs), pmemkv_errormsg()), \ 0))) #ifdef JSON_TESTS_SUPPORT pmemkv_config *C_CONFIG_FROM_JSON(const char *json) { pmemkv_config *cfg = pmemkv_config_new(); UT_ASSERTne(cfg, NULL); int s = pmemkv_config_from_json(cfg, json); if (s != PMEMKV_STATUS_OK) { UT_FATAL(pmemkv_config_from_json_errormsg()); } return cfg; } #endif /* JSON_TESTS_SUPPORT */ #ifdef __cplusplus } #endif #endif /* PMEMKV_UNITTEST_H */ pmemkv-1.5.0/tests/common/unittest.hpp000066400000000000000000000213521410000423300200240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2018-2021, Intel Corporation */ #ifndef PMEMKV_UNITTEST_HPP #define PMEMKV_UNITTEST_HPP #include "unittest.h" #include #include #ifdef JSON_TESTS_SUPPORT #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include static inline void UT_EXCEPTION(std::exception &e) { std::cerr << e.what() << std::endl; } /* assertion with exception related string printed */ #define UT_FATALexc(exception) \ ((void)(UT_EXCEPTION(exception), \ (UT_FATAL("%s:%d %s - assertion failure", __FILE__, __LINE__, __func__), \ 0))) #ifdef _WIN32 #define __PRETTY_FUNCTION__ __func__ #endif #define PRINT_TEST_PARAMS \ do { \ std::cout << "TEST: " << __PRETTY_FUNCTION__ << std::endl; \ } while (0) #define STAT(path, st) ut_stat(__FILE__, __LINE__, __func__, path, st) #define UT_ASSERT_NOEXCEPT(...) \ static_assert(noexcept(__VA_ARGS__), "Operation must be noexcept") #define STR(x) #x #define ASSERT_ALIGNED_BEGIN(type, ref) \ do { \ size_t off = 0; \ const char *last = "(none)"; #define ASSERT_ALIGNED_FIELD(type, ref, field) \ do { \ if (offsetof(type, field) != off) \ UT_FATAL( \ "%s: padding, missing field or fields not in order between " \ "'%s' and '%s' -- offset %lu, real offset %lu", \ STR(type), last, STR(field), off, \ offsetof(type, field)); \ off += sizeof((ref).field); \ last = STR(field); \ } while (0) #define ASSERT_FIELD_SIZE(field, ref, size) \ do { \ UT_COMPILE_ERROR_ON(size != sizeof((ref).field)); \ } while (0) #define ASSERT_ALIGNED_CHECK(type) \ if (off != sizeof(type)) \ UT_FATAL("%s: missing field or padding after '%s': " \ "sizeof(%s) = %lu, fields size = %lu", \ STR(type), last, STR(type), sizeof(type), off); \ } \ while (0) #define ASSERT_OFFSET_CHECKPOINT(type, checkpoint) \ do { \ if (off != checkpoint) \ UT_FATAL("%s: violated offset checkpoint -- " \ "checkpoint %lu, real offset %lu", \ STR(type), checkpoint, off); \ } while (0) #define ASSERT_STATUS(status, expected_status) \ do { \ auto current_status = status; \ UT_ASSERTeq(current_status, expected_status); \ std::string expected_string = #expected_status; \ expected_string.erase(0, expected_string.rfind(":") + 1); \ expected_string += \ " (" + std::to_string(static_cast(expected_status)) + ")"; \ std::ostringstream oss; \ oss << current_status; \ if (oss.str() != expected_string) \ UT_FATAL("%s:%d %s - wrong status message (%s), should be: %s", \ __FILE__, __LINE__, __func__, oss.str().c_str(), \ expected_string.c_str()); \ } while (0) #define ASSERT_SIZE(kv, expected_size) \ do { \ size_t cnt; \ ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); \ UT_ASSERTeq(cnt, expected_size); \ } while (0) static std::string currently_tested; static inline int run_test(std::function test) { test_register_sighandlers(); try { test(); } catch (std::exception &e) { UT_FATALexc(e); } catch (...) { UT_FATAL("catch(...){}"); } return 0; } 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(); } } /* * This function executes 'concurrency' threads and provides * 'syncthreads' method (synchronization barrier) for f() */ template void parallel_xexec(size_t concurrency, Function f) { std::condition_variable cv; std::mutex m; size_t counter = 0; auto syncthreads = [&] { std::unique_lock lock(m); counter++; if (counter < concurrency) cv.wait(lock); else /* * notify_call could be called outside of a lock * (it would perform better) but drd complains * in that case */ cv.notify_all(); }; parallel_exec(concurrency, [&](size_t tid) { f(tid, syncthreads); }); } #ifdef JSON_TESTS_SUPPORT pmem::kv::config CONFIG_FROM_JSON(std::string json) { return pmem::kv::config(C_CONFIG_FROM_JSON(json.c_str())); } #endif /* JSON_TESTS_SUPPORT */ pmem::kv::db INITIALIZE_KV(std::string engine, pmem::kv::config &&config) { currently_tested = engine; pmem::kv::db kv; auto s = kv.open(engine, std::move(config)); ASSERT_STATUS(s, pmem::kv::status::OK); return kv; } /* XXX - replace with call to actual clear() method once implemented */ void CLEAR_KV(pmem::kv::db &kv) { std::vector keys; kv.get_all([&](pmem::kv::string_view key, pmem::kv::string_view value) { keys.emplace_back(key.data(), key.size()); return 0; }); for (auto &k : keys) ASSERT_STATUS(kv.remove(k), pmem::kv::status::OK); } #ifdef JSON_TESTS_SUPPORT static inline int run_engine_tests(std::string engine, std::string json, std::vector> tests) { test_register_sighandlers(); try { auto kv = INITIALIZE_KV(engine, CONFIG_FROM_JSON(json)); for (auto &test : tests) { test(kv); CLEAR_KV(kv); } } catch (std::exception &e) { UT_FATALexc(e); } catch (...) { UT_FATAL("catch(...){}"); } return 0; } #endif /* JSON_TESTS_SUPPORT */ static inline pmem::kv::string_view uint64_to_strv(uint64_t &key) { return pmem::kv::string_view((char *)&key, sizeof(uint64_t)); } /* It creates a string that contains 8 bytes from uint64_t. */ static inline std::string uint64_to_string(uint64_t &key) { return std::string(reinterpret_cast(&key), 8); } /* It aligns entry to a certain size. It is useful for testing engines with fixed-size * keys. */ static inline std::string align_to_size(size_t size, std::string str, char char_to_fill = 'x') { if (str.size() > size) { UT_FATAL((str + " - too long entry for the engine: " + currently_tested) .c_str()); } return str + std::string(size - str.size(), char_to_fill); } /* It returns an entry filled/shrank to a certain size. It's necessary during testing * engines with fixed size keys/values */ static inline std::string entry_from_string(std::string str) { if (currently_tested == "robinhood") return align_to_size(8, str); return str; } static inline std::string entry_from_number(size_t number, std::string prefix = "", std::string postfix = "") { std::string res = prefix + std::to_string(number) + postfix; return entry_from_string(res); } #endif /* PMEMKV_UNITTEST_HPP */ pmemkv-1.5.0/tests/comparator/000077500000000000000000000000001410000423300163105ustar00rootroot00000000000000pmemkv-1.5.0/tests/comparator/basic.c000066400000000000000000000037001410000423300175350ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include #include "unittest.h" #include static int ARG_VALUE = 0xABC; static int reverse_three_way_compare(const char *key1, size_t keybytes1, const char *key2, size_t keybytes2, void *arg) { UT_ASSERT(*((int *)arg) == ARG_VALUE); /* Compare just first bytes */ return key2[0] - key1[0]; } static const char *keys[3]; static int keys_count = 0; static int get_callback(const char *key, size_t kb, const char *value, size_t vb, void *arg) { char *key_copy = malloc(kb); memcpy(key_copy, key, kb); keys[keys_count++] = key_copy; return 0; } static void test_valid_comparator(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = pmemkv_comparator_new(&reverse_three_way_compare, "single_byte_compare", &ARG_VALUE); UT_ASSERTne(cmp, NULL); int s = pmemkv_config_put_comparator(cfg, cmp); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_db *db; s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "123", 3, "1", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "333", 3, "1", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "223", 3, "1", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_get_all(db, &get_callback, NULL); UT_ASSERTeq(keys_count, 3); UT_ASSERTeq(memcmp(keys[0], "333", 3), 0); UT_ASSERTeq(memcmp(keys[1], "223", 3), 0); UT_ASSERTeq(memcmp(keys[2], "123", 3), 0); pmemkv_close(db); } static void test_nullptr_function(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = pmemkv_comparator_new(NULL, "name", &ARG_VALUE); UT_ASSERTeq(cmp, NULL); pmemkv_config_delete(cfg); } int main(int argc, char *argv[]) { START(); if (argc < 3) UT_FATAL("usage %s: engine config", argv[0]); test_valid_comparator(argv[1], C_CONFIG_FROM_JSON(argv[2])); test_nullptr_function(argv[1], C_CONFIG_FROM_JSON(argv[2])); return 0; } pmemkv-1.5.0/tests/comparator/basic_persistent.c000066400000000000000000000014671410000423300220250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include #include "unittest.h" #include static int ARG_VALUE = 0xABC; static int reverse_three_way_compare(const char *key1, size_t keybytes1, const char *key2, size_t keybytes2, void *arg) { UT_ASSERT(*((int *)arg) == ARG_VALUE); /* Compare just first bytes */ return key2[0] - key1[0]; } static void test_nullptr_name(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = pmemkv_comparator_new(&reverse_three_way_compare, NULL, &ARG_VALUE); UT_ASSERTeq(cmp, NULL); pmemkv_config_delete(cfg); } int main(int argc, char *argv[]) { START(); if (argc < 3) UT_FATAL("usage %s: engine config", argv[0]); test_nullptr_name(argv[1], C_CONFIG_FROM_JSON(argv[2])); return 0; } pmemkv-1.5.0/tests/comparator/custom_reopen.c000066400000000000000000000052251410000423300213420ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include #include #include "unittest.h" static const char *EXPECTED_ERR_MSG = "[pmemkv_open] Comparator with name: \"valid_cmp\" expected"; int compare(const char *k1, size_t kb1, const char *k2, size_t kb2, void *arg) { UT_ASSERT(kb1 == 1 && kb2 == 1); return k1[0] - k2[0]; } static pmemkv_comparator *create_cmp(const char *name) { pmemkv_comparator *cmp = pmemkv_comparator_new(compare, name, NULL); UT_ASSERTne(cmp, NULL); return cmp; } static void insert(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = create_cmp("valid_cmp"); int s = pmemkv_config_put_comparator(cfg, cmp); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_db *db; s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "A", 1, "A", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "B", 1, "B", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "C", 1, "C", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "D", 1, "D", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_close(db); } static void check_valid(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = create_cmp("valid_cmp"); int s = pmemkv_config_put_comparator(cfg, (void *)cmp); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_db *db; s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_OK); size_t cnt = UINT64_MAX; s = pmemkv_count_above(db, "B", 1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_OK); UT_ASSERTeq(cnt, 2); cnt = UINT64_MAX; s = pmemkv_count_below(db, "B", 1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_OK); UT_ASSERTeq(cnt, 1); pmemkv_close(db); } static void check_invalid(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = create_cmp("invalid_cmp"); int s = pmemkv_config_put_comparator(cfg, (void *)cmp); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_db *db; s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_COMPARATOR_MISMATCH); const char *err = pmemkv_errormsg(); UT_ASSERT(strcmp(err, EXPECTED_ERR_MSG) == 0); } int main(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); const char *engine = argv[1]; const char *json_config = argv[2]; const char *mode = argv[3]; if (strcmp(mode, "insert") != 0 && strcmp(mode, "check") != 0) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); if (strcmp(mode, "insert") == 0) { insert(engine, C_CONFIG_FROM_JSON(json_config)); } else { check_valid(engine, C_CONFIG_FROM_JSON(json_config)); check_invalid(engine, C_CONFIG_FROM_JSON(json_config)); } return 0; } pmemkv-1.5.0/tests/comparator/custom_reopen.cc000066400000000000000000000046701410000423300215100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "unittest.hpp" #include using namespace pmem::kv; static const char *EXPECTED_ERR_MSG = "[pmemkv_open] Comparator with name: \"valid_cmp\" expected"; class valid_comparator { public: int compare(string_view k1, string_view k2) { return k1.compare(k2); } std::string name() { return "valid_cmp"; } }; class invalid_comparator { public: int compare(string_view k1, string_view k2) { return k2.compare(k1); } std::string name() { return "invalid_cmp"; } }; static void insert(std::string name, pmem::kv::config &&cfg) { auto s = cfg.put_comparator(valid_comparator{}); ASSERT_STATUS(s, status::OK); pmem::kv::db kv; s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::OK); s = kv.put("A", "A"); ASSERT_STATUS(s, status::OK); s = kv.put("B", "B"); ASSERT_STATUS(s, status::OK); s = kv.put("C", "C"); ASSERT_STATUS(s, status::OK); s = kv.put("D", "D"); ASSERT_STATUS(s, status::OK); kv.close(); } static void check_valid(std::string name, pmem::kv::config &&cfg) { auto s = cfg.put_comparator(valid_comparator{}); ASSERT_STATUS(s, status::OK); pmem::kv::db kv; s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::OK); size_t cnt = std::numeric_limits::max(); s = kv.count_above("B", cnt); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(cnt, 2); cnt = std::numeric_limits::max(); s = kv.count_below("B", cnt); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(cnt, 1); kv.close(); } static void check_invalid(std::string name, pmem::kv::config &&cfg) { auto s = cfg.put_comparator(invalid_comparator{}); ASSERT_STATUS(s, status::OK); pmem::kv::db kv; s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::COMPARATOR_MISMATCH); UT_ASSERT(pmem::kv::errormsg() == EXPECTED_ERR_MSG); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string engine = argv[1]; std::string json_config = argv[2]; std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); if (mode == "insert") { insert(engine, CONFIG_FROM_JSON(json_config)); } else { check_valid(engine, CONFIG_FROM_JSON(json_config)); check_invalid(engine, CONFIG_FROM_JSON(json_config)); } } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/comparator/default_reopen.c000066400000000000000000000046301410000423300214530ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include #include #include "unittest.h" static const char *EXPECTED_ERR_MSG = "[pmemkv_open] Comparator with name: \"__pmemkv_binary_comparator\" expected"; int compare(const char *k1, size_t kb1, const char *k2, size_t kb2, void *arg) { return 0; } static pmemkv_comparator *create_cmp(const char *name) { pmemkv_comparator *cmp = pmemkv_comparator_new(compare, name, NULL); UT_ASSERTne(cmp, NULL); return cmp; } static void insert(const char *engine, pmemkv_config *cfg) { pmemkv_db *db; int s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "A", 1, "A", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "B", 1, "B", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "C", 1, "C", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); s = pmemkv_put(db, "D", 1, "D", 1); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_close(db); } static void check_valid(const char *engine, pmemkv_config *cfg) { pmemkv_db *db; int s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_OK); size_t cnt = UINT64_MAX; s = pmemkv_count_above(db, "B", 1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_OK); UT_ASSERTeq(cnt, 2); cnt = UINT64_MAX; s = pmemkv_count_below(db, "B", 1, &cnt); UT_ASSERTeq(s, PMEMKV_STATUS_OK); UT_ASSERTeq(cnt, 1); pmemkv_close(db); } static void check_invalid(const char *engine, pmemkv_config *cfg) { pmemkv_comparator *cmp = create_cmp("invalid_cmp"); int s = pmemkv_config_put_object(cfg, "comparator", (void *)cmp, (void(*))pmemkv_comparator_delete); UT_ASSERTeq(s, PMEMKV_STATUS_OK); pmemkv_db *db; s = pmemkv_open(engine, cfg, &db); UT_ASSERTeq(s, PMEMKV_STATUS_COMPARATOR_MISMATCH); const char *err = pmemkv_errormsg(); UT_ASSERT(strcmp(err, EXPECTED_ERR_MSG) == 0); } int main(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); const char *engine = argv[1]; const char *json_config = argv[2]; const char *mode = argv[3]; if (strcmp(mode, "insert") != 0 && strcmp(mode, "check") != 0) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); if (strcmp(mode, "insert") == 0) { insert(engine, C_CONFIG_FROM_JSON(json_config)); } else { check_valid(engine, C_CONFIG_FROM_JSON(json_config)); check_invalid(engine, C_CONFIG_FROM_JSON(json_config)); } return 0; } pmemkv-1.5.0/tests/comparator/default_reopen.cc000066400000000000000000000042141410000423300216140ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "unittest.hpp" #include using namespace pmem::kv; static const char *EXPECTED_ERR_MSG = "[pmemkv_open] Comparator with name: \"__pmemkv_binary_comparator\" expected"; class invalid_comparator { public: int compare(string_view k1, string_view k2) { return k2.compare(k1); } std::string name() { return "invalid_cmp"; } }; static void insert(std::string name, pmem::kv::config &&cfg) { pmem::kv::db kv; auto s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::OK); s = kv.put("A", "A"); ASSERT_STATUS(s, status::OK); s = kv.put("B", "B"); ASSERT_STATUS(s, status::OK); s = kv.put("C", "C"); ASSERT_STATUS(s, status::OK); s = kv.put("D", "D"); ASSERT_STATUS(s, status::OK); kv.close(); } static void check_valid(std::string name, pmem::kv::config &&cfg) { pmem::kv::db kv; auto s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::OK); size_t cnt = std::numeric_limits::max(); s = kv.count_above("B", cnt); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(cnt, 2); cnt = std::numeric_limits::max(); s = kv.count_below("B", cnt); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(cnt, 1); kv.close(); } static void check_invalid(std::string name, pmem::kv::config &&cfg) { auto s = cfg.put_comparator(invalid_comparator{}); ASSERT_STATUS(s, status::OK); pmem::kv::db kv; s = kv.open(name, std::move(cfg)); ASSERT_STATUS(s, status::COMPARATOR_MISMATCH); UT_ASSERT(pmem::kv::errormsg() == EXPECTED_ERR_MSG); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string engine = argv[1]; std::string json_config = argv[2]; std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); if (mode == "insert") { insert(engine, CONFIG_FROM_JSON(json_config)); } else { check_valid(engine, CONFIG_FROM_JSON(json_config)); check_invalid(engine, CONFIG_FROM_JSON(json_config)); } } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/compatibility/000077500000000000000000000000001410000423300170125ustar00rootroot00000000000000pmemkv-1.5.0/tests/compatibility/CMakeLists.txt000066400000000000000000000011121410000423300215450ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation project(pmemkv_compatibility_test) cmake_minimum_required(VERSION 3.13) 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.5.0/tests/compatibility/README.md000066400000000000000000000002351410000423300202710ustar00rootroot00000000000000This folder contains tests which can be used to test compatibility between different pmemkv versions. For usage, see ../../utils/docker/run-compatibility.sh pmemkv-1.5.0/tests/compatibility/cmap.cc000066400000000000000000000046371410000423300202530ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #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("create_or_error_if_exists", 1); assert(s == pmem::kv::status::OK); /* force_create flag left here for compatibility. This test's binary * is used to create/open also with older pmemkv versions. */ 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.5.0/tests/compatibility/cmap.sh000077500000000000000000000015141410000423300202720ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation # # cmap.sh -- runs cmap compatibility test # set -e binary1=${1} binary2=${2} testfile=${3} echo "## cmap.sh" echo "1st binary: ${binary1}" echo "2nd binary: ${binary2}" echo "testfile: ${testfile}" echo echo "Test: binary1 create; binary2 open" rm -f ${testfile} ${binary1} ${testfile} create ${binary2} ${testfile} open echo "Test: binary1 create_ungraceful; binary2 open" rm -f ${testfile} ${binary1} ${testfile} create_ungraceful ${binary2} ${testfile} open echo "Test: binary2 create; binary1 open" rm -f ${testfile} ${binary2} ${testfile} create ${binary1} ${testfile} open echo "Test: binary2 create_ungraceful; binary1 open" rm -f ${testfile} ${binary2} ${testfile} create_ungraceful ${binary1} ${testfile} open rm -f ${testfile} pmemkv-1.5.0/tests/config/000077500000000000000000000000001410000423300154065ustar00rootroot00000000000000pmemkv-1.5.0/tests/config/config_c.c000066400000000000000000000310641410000423300173250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include "unittest.h" #include /** * Tests all config methods using C API */ static const int TEST_VAL = 0xABC; static const int INIT_VAL = 1; static const int DELETED_VAL = 2; static const char *PATH = "/some/path"; static const uint64_t SIZE = 0xDEADBEEF; struct custom_type { int a; char b; }; struct custom_type_wrapper { struct custom_type value; int additional_state; }; static void *getter(void *arg) { struct custom_type_wrapper *ct = (struct custom_type_wrapper *)(arg); return &ct->value; } static void deleter(struct custom_type *ct_ptr) { ct_ptr->a = DELETED_VAL; ct_ptr->b = DELETED_VAL; } static void xdeleter(struct custom_type_wrapper *ct_ptr) { ct_ptr->value.a = DELETED_VAL; ct_ptr->value.b = DELETED_VAL; ct_ptr->additional_state = DELETED_VAL; } static void simple_test() { /** * TEST: add and read data from config, using basic functions */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); int ret = pmemkv_config_put_string(config, "string", "abc"); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_int64(config, "int", 123); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); struct custom_type *ptr = malloc(sizeof(struct custom_type)); ptr->a = INIT_VAL; ptr->b = INIT_VAL; ret = pmemkv_config_put_object(config, "object_ptr", ptr, NULL); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_data(config, "object", ptr, sizeof(*ptr)); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); struct custom_type *ptr_deleter = malloc(sizeof(struct custom_type)); ptr_deleter->a = INIT_VAL; ptr_deleter->b = INIT_VAL; ret = pmemkv_config_put_object(config, "object_ptr_with_deleter", ptr_deleter, (void (*)(void *)) & deleter); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_path(config, PATH); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_size(config, SIZE); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_create_or_error_if_exists(config, true); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); const char *value_string; ret = pmemkv_config_get_string(config, "string", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERT(strcmp(value_string, "abc") == 0); int64_t value_int; ret = pmemkv_config_get_int64(config, "int", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_int, 123); struct custom_type *value_custom_ptr; ret = pmemkv_config_get_object(config, "object_ptr", (void **)&value_custom_ptr); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_custom_ptr->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr->b, INIT_VAL); struct custom_type *value_custom_ptr_deleter; ret = pmemkv_config_get_object(config, "object_ptr_with_deleter", (void **)&value_custom_ptr_deleter); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_custom_ptr_deleter->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr_deleter->b, INIT_VAL); struct custom_type *value_custom; size_t value_custom_size; ret = pmemkv_config_get_data(config, "object", (const void **)&value_custom, &value_custom_size); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_custom_size, sizeof(value_custom)); UT_ASSERTeq(value_custom->a, INIT_VAL); UT_ASSERTeq(value_custom->b, INIT_VAL); int64_t none; UT_ASSERTeq(pmemkv_config_get_int64(config, "non-existent", &none), PMEMKV_STATUS_NOT_FOUND); ret = pmemkv_config_get_string(config, "path", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERT(strcmp(value_string, PATH) == 0); uint64_t value_uint; ret = pmemkv_config_get_uint64(config, "size", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_uint, SIZE); ret = pmemkv_config_get_uint64(config, "create_or_error_if_exists", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_uint, 1); pmemkv_config_delete(config); config = NULL; UT_ASSERTeq(value_custom_ptr_deleter->a, DELETED_VAL); UT_ASSERTeq(value_custom_ptr_deleter->b, DELETED_VAL); /* deleter was not set */ UT_ASSERTeq(ptr, value_custom_ptr); UT_ASSERTeq(value_custom_ptr->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr->b, INIT_VAL); free(ptr); free(ptr_deleter); } static void put_oid_simple_test() { /** * TEST: basic check for put_oid function. */ pmemkv_config *cfg = pmemkv_config_new(); UT_ASSERT(cfg != NULL); PMEMoid oid; int ret = pmemkv_config_put_oid(cfg, &oid); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); PMEMoid *oid_ptr; ret = pmemkv_config_get_object(cfg, "oid", (void **)&oid_ptr); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(&oid, oid_ptr); pmemkv_config_delete(cfg); } static void free_deleter_test() { /** * TEST: checks if put_object will work with 'free' command as deleter */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); struct custom_type *ptr = malloc(sizeof(struct custom_type)); ptr->a = INIT_VAL; ptr->b = INIT_VAL; int ret = pmemkv_config_put_object(config, "object_ptr", ptr, free); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); pmemkv_config_delete(config); } static void ex_put_object_test() { /** * TEST: checks if put_object_cb's deleter with additional state is working * properly */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); struct custom_type_wrapper *ptr = malloc(sizeof(struct custom_type_wrapper)); ptr->value.a = INIT_VAL; ptr->value.b = INIT_VAL; ptr->additional_state = TEST_VAL; int ret = pmemkv_config_put_object_cb(config, "object_ptr", ptr, (void *(*)(void *))getter, (void (*)(void *))xdeleter); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); struct custom_type *ptr_from_get; pmemkv_config_get_object(config, "object_ptr", (void **)&ptr_from_get); UT_ASSERTeq(ptr_from_get->a, INIT_VAL); UT_ASSERTeq(ptr_from_get->b, INIT_VAL); pmemkv_config_delete(config); config = NULL; UT_ASSERTeq(ptr->value.a, DELETED_VAL); UT_ASSERTeq(ptr->value.b, DELETED_VAL); UT_ASSERTeq(ptr->additional_state, DELETED_VAL); free(ptr); } static void ex_put_object_nullptr_del_test() { /** * TEST: checs if put_object_cb will work with nullptr deleter */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); struct custom_type_wrapper *ptr = malloc(sizeof(struct custom_type_wrapper)); ptr->value.a = INIT_VAL; ptr->value.b = INIT_VAL; ptr->additional_state = TEST_VAL; int ret = pmemkv_config_put_object_cb(config, "object_ptr", ptr, (void *(*)(void *))getter, NULL); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); pmemkv_config_delete(config); config = NULL; UT_ASSERTeq(ptr->value.a, INIT_VAL); UT_ASSERTeq(ptr->value.b, INIT_VAL); UT_ASSERTeq(ptr->additional_state, TEST_VAL); free(ptr); } static void ex_put_object_nullptr_getter_test() { /** * TEST: put_object_cb should not work with nullptr getter function */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); struct custom_type_wrapper *ptr = malloc(sizeof(struct custom_type_wrapper)); ptr->value.a = INIT_VAL; ptr->value.b = INIT_VAL; ptr->additional_state = TEST_VAL; int ret = pmemkv_config_put_object_cb(config, "object_ptr", ptr, NULL, NULL); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); pmemkv_config_delete(config); free(ptr); } static void ex_put_object_free_del_test() { /** * TEST: checks if put_object_cb will work with 'free' command as deleter */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); struct custom_type_wrapper *ptr = malloc(sizeof(struct custom_type_wrapper)); ptr->value.a = INIT_VAL; ptr->value.b = INIT_VAL; ptr->additional_state = TEST_VAL; int ret = pmemkv_config_put_object_cb(config, "object_ptr", ptr, (void *(*)(void *))getter, free); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); pmemkv_config_delete(config); config = NULL; } static void integral_conversion_test() { /** * TEST: when reading data from config it's allowed to read integers * into different type (then it was originally stored), as long as * the conversion is possible. CONFIG_TYPE_ERROR should be returned * when e.g. reading negative integral value into signed int type. */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); int ret = pmemkv_config_put_int64(config, "int", 123); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_uint64(config, "uint", 123); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_int64(config, "negative-int", -123); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); ret = pmemkv_config_put_uint64(config, "uint-max", (uint64_t)-1); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); int64_t int_s; ret = pmemkv_config_get_int64(config, "int", &int_s); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(int_s, 123); size_t int_us; ret = pmemkv_config_get_uint64(config, "int", &int_us); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(int_us, 123U); int64_t uint_s; ret = pmemkv_config_get_int64(config, "uint", &uint_s); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(uint_s, 123); size_t uint_us; ret = pmemkv_config_get_uint64(config, "uint", &uint_us); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(uint_us, 123U); int64_t neg_int_s; ret = pmemkv_config_get_int64(config, "negative-int", &neg_int_s); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(neg_int_s, -123); size_t neg_int_us; ret = pmemkv_config_get_uint64(config, "negative-int", &neg_int_us); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); int64_t uint_max_s; ret = pmemkv_config_get_int64(config, "uint-max", &uint_max_s); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); size_t uint_max_us; ret = pmemkv_config_get_uint64(config, "uint-max", &uint_max_us); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(uint_max_us, ((uint64_t)-1)); pmemkv_config_delete(config); } static void not_found_test() { /** * TEST: all config get_* methods should return status NOT_FOUND if item * does not exist */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); const char *my_string; int ret = pmemkv_config_get_string(config, "non-existent-string", &my_string); UT_ASSERTeq(ret, PMEMKV_STATUS_NOT_FOUND); int64_t my_int; ret = pmemkv_config_get_int64(config, "non-existent-int", &my_int); UT_ASSERTeq(ret, PMEMKV_STATUS_NOT_FOUND); size_t my_uint; ret = pmemkv_config_get_uint64(config, "non-existent-uint", &my_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_NOT_FOUND); struct custom_type *my_object; ret = pmemkv_config_get_object(config, "non-existent-object", (void **)&my_object); UT_ASSERTeq(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); UT_ASSERTeq(ret, PMEMKV_STATUS_NOT_FOUND); UT_ASSERTeq(my_object_size, 0U); pmemkv_config_delete(config); } static void null_config_test() { /** * TEST: in C API all config methods require 'config' as param - it can't be null */ int ret = pmemkv_config_put_string(NULL, "string", "abc"); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); ret = pmemkv_config_put_int64(NULL, "int", 123); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); ret = pmemkv_config_put_uint64(NULL, "uint", 123456); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); struct custom_type *ptr = malloc(sizeof(struct custom_type)); ptr->a = INIT_VAL; ptr->b = INIT_VAL; ret = pmemkv_config_put_object(NULL, "object_ptr", ptr, NULL); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); ret = pmemkv_config_put_data(NULL, "object", ptr, sizeof(*ptr)); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); const char *value_string; ret = pmemkv_config_get_string(NULL, "string", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); int64_t value_int; ret = pmemkv_config_get_int64(NULL, "int", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); uint64_t value_uint; ret = pmemkv_config_get_uint64(NULL, "uint", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); struct custom_type *value_custom_ptr; ret = pmemkv_config_get_object(NULL, "object_ptr", (void **)&value_custom_ptr); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); struct custom_type *value_custom; size_t value_custom_size; ret = pmemkv_config_get_data(NULL, "object", (const void **)&value_custom, &value_custom_size); UT_ASSERTeq(ret, PMEMKV_STATUS_INVALID_ARGUMENT); free(ptr); } int main(int argc, char *argv[]) { START(); simple_test(); put_oid_simple_test(); free_deleter_test(); ex_put_object_test(); ex_put_object_nullptr_del_test(); ex_put_object_free_del_test(); ex_put_object_nullptr_getter_test(); integral_conversion_test(); not_found_test(); null_config_test(); return 0; } pmemkv-1.5.0/tests/config/config_cpp.cc000066400000000000000000000274741410000423300200420ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include "unittest.hpp" #include #include /** * Tests all config methods using C++ API */ using namespace pmem::kv; static const int INIT_VAL = 1; static const int DELETED_VAL = 2; static const char *PATH = "/some/path"; static const uint64_t SIZE = 0xDEADBEEF; struct custom_type { int a; char b; }; static void deleter(custom_type *ct_ptr) { ct_ptr->a = DELETED_VAL; ct_ptr->b = DELETED_VAL; } static void simple_test() { /** * TEST: add and read data from config, using basic methods. */ auto cfg = new config; UT_ASSERT(cfg != nullptr); status s = cfg->put_string("string", "abc"); ASSERT_STATUS(s, status::OK); s = cfg->put_int64("int", 123); ASSERT_STATUS(s, status::OK); custom_type *ptr = new custom_type; ptr->a = INIT_VAL; ptr->b = INIT_VAL; s = cfg->put_object("object_ptr", ptr, nullptr); ASSERT_STATUS(s, status::OK); s = cfg->put_data("object", ptr); ASSERT_STATUS(s, status::OK); int array[3] = {1, 15, 77}; s = cfg->put_data("array", array, 3); ASSERT_STATUS(s, status::OK); custom_type *ptr_deleter = new custom_type; ptr_deleter->a = INIT_VAL; ptr_deleter->b = INIT_VAL; s = cfg->put_object("object_ptr_with_deleter", ptr_deleter, (void (*)(void *)) & deleter); ASSERT_STATUS(s, status::OK); s = cfg->put_path(PATH); ASSERT_STATUS(s, status::OK); s = cfg->put_size(SIZE); ASSERT_STATUS(s, status::OK); s = cfg->put_create_or_error_if_exists(true); ASSERT_STATUS(s, status::OK); s = cfg->put_create_if_missing(true); ASSERT_STATUS(s, status::OK); std::string value_string; s = cfg->get_string("string", value_string); ASSERT_STATUS(s, status::OK); UT_ASSERT(value_string == "abc"); int64_t value_int; s = cfg->get_int64("int", value_int); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(value_int, 123); custom_type *value_custom_ptr; s = cfg->get_object("object_ptr", value_custom_ptr); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(value_custom_ptr->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr->b, INIT_VAL); custom_type *value_custom_ptr_deleter; s = cfg->get_object("object_ptr_with_deleter", value_custom_ptr_deleter); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(value_custom_ptr_deleter->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr_deleter->b, INIT_VAL); custom_type *value_custom; size_t value_custom_count = 0; s = cfg->get_data("object", value_custom, value_custom_count); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(value_custom_count, 1U); UT_ASSERTeq(value_custom->a, INIT_VAL); UT_ASSERTeq(value_custom->b, INIT_VAL); int *value_array; size_t value_array_count = 0; s = cfg->get_data("array", value_array, value_array_count); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(value_array_count, 3U); UT_ASSERTeq(value_array[0], 1); UT_ASSERTeq(value_array[1], 15); UT_ASSERTeq(value_array[2], 77); int64_t none; ASSERT_STATUS(cfg->get_int64("non-existent", none), status::NOT_FOUND); s = cfg->get_string("path", value_string); ASSERT_STATUS(s, status::OK); UT_ASSERT(value_string == PATH); uint64_t int_us; s = cfg->get_uint64("size", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, SIZE); s = cfg->get_uint64("create_or_error_if_exists", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 1); s = cfg->get_uint64("create_if_missing", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 1); delete cfg; cfg = nullptr; UT_ASSERTeq(value_custom_ptr_deleter->a, DELETED_VAL); UT_ASSERTeq(value_custom_ptr_deleter->b, DELETED_VAL); /* delete was not set */ UT_ASSERTeq(ptr, value_custom_ptr); UT_ASSERTeq(value_custom_ptr->a, INIT_VAL); UT_ASSERTeq(value_custom_ptr->b, INIT_VAL); delete ptr; delete ptr_deleter; } static void put_edge_cases() { /** * TEST: Edge cases input data for some methods. */ auto cfg = new config; auto s = cfg->put_create_or_error_if_exists(false); ASSERT_STATUS(s, status::OK); s = cfg->put_create_if_missing(false); ASSERT_STATUS(s, status::OK); uint64_t int_us; s = cfg->get_uint64("create_or_error_if_exists", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 0); s = cfg->get_uint64("create_if_missing", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 0); auto max_size = std::numeric_limits::max(); s = cfg->put_size(max_size); ASSERT_STATUS(s, status::OK); s = cfg->get_uint64("size", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, max_size); delete cfg; /* Some of those strings are not real paths, but config should not crash on them * */ std::vector paths = {" ", "", "//", ",./;'[]-=<>?:\"{}|_+!@#$%^&*()`~", "/👾"}; for (auto path : paths) { auto cfg = new config(); s = cfg->put_path(path); ASSERT_STATUS(s, status::OK); std::string value_string; s = cfg->get_string("path", value_string); ASSERT_STATUS(s, status::OK); UT_ASSERT(value_string == path); delete cfg; } } static void put_oid_simple_test() { /** * TEST: basic check for put_oid method. */ auto cfg = new config; UT_ASSERT(cfg != NULL); PMEMoid oid; status ret = cfg->put_oid(&oid); ASSERT_STATUS(ret, status::OK); PMEMoid *oid_ptr; ret = cfg->get_object("oid", oid_ptr); ASSERT_STATUS(ret, status::OK); UT_ASSERTeq(&oid, oid_ptr); delete cfg; } static void object_unique_ptr_default_deleter_test() { auto cfg = new config; UT_ASSERT(cfg != nullptr); auto ptr_default = std::unique_ptr(new custom_type); ptr_default->a = INIT_VAL; ptr_default->b = INIT_VAL; auto s = cfg->put_object("object_ptr", std::move(ptr_default)); ASSERT_STATUS(s, status::OK); delete cfg; } static void object_unique_ptr_nullptr_test() { auto cfg = new config; UT_ASSERT(cfg != nullptr); auto ptr = std::unique_ptr(nullptr); auto s = cfg->put_object("object_ptr", std::move(ptr)); ASSERT_STATUS(s, status::OK); custom_type *raw_ptr; s = cfg->get_object("object_ptr", raw_ptr); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(raw_ptr, nullptr); delete cfg; } static void object_unique_ptr_custom_deleter_test() { auto cfg = new config; UT_ASSERT(cfg != nullptr); auto custom_deleter = [&](custom_type *ptr) { ptr->a = DELETED_VAL; ptr->b = DELETED_VAL; }; auto ptr_custom = std::unique_ptr( new custom_type, custom_deleter); ptr_custom->a = INIT_VAL; ptr_custom->b = INIT_VAL; auto *raw_ptr = ptr_custom.get(); auto s = cfg->put_object("object_ptr", std::move(ptr_custom)); ASSERT_STATUS(s, status::OK); delete cfg; UT_ASSERTeq(raw_ptr->a, DELETED_VAL); UT_ASSERTeq(raw_ptr->b, DELETED_VAL); delete raw_ptr; } static void integral_conversion_test() { /** * TEST: when reading data from config it's allowed to read integers * into different type (then it was originally stored), as long as * the conversion is possible. CONFIG_TYPE_ERROR should be returned * when e.g. reading negative integral value into signed int type. */ auto cfg = new config; UT_ASSERT(cfg != nullptr); status s = cfg->put_int64("int", 123); ASSERT_STATUS(s, status::OK); s = cfg->put_uint64("uint", 123); ASSERT_STATUS(s, status::OK); s = cfg->put_int64("negative-int", -123); ASSERT_STATUS(s, status::OK); s = cfg->put_uint64("uint-max", std::numeric_limits::max()); ASSERT_STATUS(s, status::OK); int64_t int_s; s = cfg->get_int64("int", int_s); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_s, 123); size_t int_us; s = cfg->get_uint64("int", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 123U); int64_t uint_s; s = cfg->get_int64("uint", uint_s); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(uint_s, 123); size_t uint_us; s = cfg->get_uint64("uint", uint_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(uint_us, 123U); int64_t neg_int_s; s = cfg->get_int64("negative-int", neg_int_s); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(neg_int_s, -123); size_t neg_int_us; s = cfg->get_uint64("negative-int", neg_int_us); ASSERT_STATUS(s, status::CONFIG_TYPE_ERROR); int64_t uint_max_s; s = cfg->get_int64("uint-max", uint_max_s); ASSERT_STATUS(s, status::CONFIG_TYPE_ERROR); size_t uint_max_us; s = cfg->get_uint64("uint-max", uint_max_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(uint_max_us, std::numeric_limits::max()); delete cfg; } static void constructors_test() { /** * TEST: in C++ API there is more than one way to create config's object */ auto cfg = new config; UT_ASSERT(cfg != nullptr); /* assign released C++ config to C config; * it's null because config is lazy initialized */ pmemkv_config *c_cfg = cfg->release(); UT_ASSERTeq(c_cfg, nullptr); /* put value to C++ config */ auto s = cfg->put_int64("int", 65535); ASSERT_STATUS(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_STATUS(s, status::OK); UT_ASSERTeq(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); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(int_s, 65535); /* check if moved config is empty */ s = move_config->get_int64("int", int_s); ASSERT_STATUS(s, status::NOT_FOUND); /* create new config... */ delete cfg; cfg = new config(); s = cfg->put_string("string", "config"); ASSERT_STATUS(s, status::OK); config *move_assign = new config(); s = move_assign->put_string("move_string", "value"); ASSERT_STATUS(s, status::OK); /* ... and check move assignment operator from different and the same config */ *move_assign = std::move(*cfg); auto &move_assign2 = *move_assign; *move_assign = std::move(move_assign2); std::string string_s; s = move_assign->get_string("move_string", string_s); ASSERT_STATUS(s, status::NOT_FOUND); s = move_assign->get_string("string", string_s); ASSERT_STATUS(s, status::OK); UT_ASSERT(string_s == "config"); /* cleanup */ pmemkv_config_delete(c_cfg); delete move_config; delete move_assign; delete cfg; } static void not_found_test() { /** * TEST: all config get_* methods should return status NOT_FOUND if item * does not exist */ auto cfg = new config; UT_ASSERT(cfg != nullptr); /* 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_STATUS(cfg->get_string("string", my_string), status::NOT_FOUND); ASSERT_STATUS(cfg->get_int64("int", my_int), status::NOT_FOUND); ASSERT_STATUS(cfg->get_uint64("uint", my_uint), status::NOT_FOUND); ASSERT_STATUS(cfg->get_object("object", my_object), status::NOT_FOUND); ASSERT_STATUS(cfg->get_data("data", my_object, my_object_count), status::NOT_FOUND); UT_ASSERTeq(my_object_count, 0U); /* initialize config with any put */ cfg->put_int64("init", 0); /* all gets should return NOT_FOUND when looking for non-existing key */ ASSERT_STATUS(cfg->get_string("non-existent-string", my_string), status::NOT_FOUND); ASSERT_STATUS(cfg->get_int64("non-existent-int", my_int), status::NOT_FOUND); ASSERT_STATUS(cfg->get_uint64("non-existent-uint", my_uint), status::NOT_FOUND); ASSERT_STATUS(cfg->get_object("non-existent-object_ptr", my_object), status::NOT_FOUND); ASSERT_STATUS(cfg->get_data("non-existent-data", my_object, my_object_count), status::NOT_FOUND); UT_ASSERTeq(my_object_count, 0U); delete cfg; } /* XXX: add tests for putting binary (and perhaps) random data into config */ static void test(int argc, char *argv[]) { simple_test(); put_oid_simple_test(); put_edge_cases(); object_unique_ptr_nullptr_test(); object_unique_ptr_default_deleter_test(); object_unique_ptr_custom_deleter_test(); integral_conversion_test(); not_found_test(); constructors_test(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/config/deprecated_config.c000066400000000000000000000016431410000423300212030ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include "unittest.h" /** * Tests deprecated config methods using C API */ static void deprecated_funcs_test() { /** * TEST: add and read data from config, using deprecated functions */ pmemkv_config *config = pmemkv_config_new(); UT_ASSERT(config != NULL); int ret = pmemkv_config_put_force_create(config, true); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); uint64_t value_uint; ret = pmemkv_config_get_uint64(config, "create_or_error_if_exists", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_uint, 1); /* Check if not-deprecated function sets the same config field */ ret = pmemkv_config_put_create_or_error_if_exists(config, false); UT_ASSERTne(ret, PMEMKV_STATUS_OK); pmemkv_config_delete(config); config = NULL; } int main(int argc, char *argv[]) { START(); deprecated_funcs_test(); return 0; } pmemkv-1.5.0/tests/config/deprecated_config.cc000066400000000000000000000013761410000423300213510ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include "unittest.hpp" /** * Tests deprecated config methods using C++ API */ using namespace pmem::kv; static void deprecated_funcs_test() { /** * TEST: add and read data from config, using deprecated methods. */ config cfg; status s = cfg.put_force_create(true); ASSERT_STATUS(s, status::OK); uint64_t int_us; s = cfg.get_uint64("create_or_error_if_exists", int_us); ASSERT_STATUS(s, status::OK); UT_ASSERTeq(int_us, 1); /* Check if not-deprecated function sets the same config field */ s = cfg.put_create_or_error_if_exists(false); UT_ASSERTne(s, status::OK); } int main(int argc, char *argv[]) { return run_test([&] { deprecated_funcs_test(); }); } pmemkv-1.5.0/tests/config/json_to_config.cc000066400000000000000000000105571410000423300207250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include #include #include "unittest.hpp" /** * Tests pmemkv_config_from_json method in C API */ static void simple_test() { /** * TEST: basic data types put into json, to be read using * pmemkv_config_from_json() */ auto config = pmemkv_config_new(); UT_ASSERT(config != nullptr); auto ret = pmemkv_config_from_json( config, "{\"int64oversize\": 10000000000000000000000 }"); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); ret = pmemkv_config_from_json( config, "{\"string\": \"abc\", \"int\": 123, \"int_neg\": -1025, " "\"bool\": true, \"bool_f\": false, " "\"sub_config\": {\"path\": \"/my/path\", \"size\": 1024000000} }"); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); const char *value_string; ret = pmemkv_config_get_string(config, "string", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERT(std::string(value_string) == "abc"); int64_t value_int; uint64_t value_uint; ret = pmemkv_config_get_uint64(config, "int", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_uint, 123); ret = pmemkv_config_get_int64(config, "int", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_int, 123); ret = pmemkv_config_get_int64(config, "int_neg", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_int, -1025); int64_t value_bool; ret = pmemkv_config_get_int64(config, "bool", &value_bool); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_bool, 1); ret = pmemkv_config_get_int64(config, "bool_f", &value_bool); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_bool, 0); pmemkv_config *sub_config; pmemkv_config_get_object(config, "sub_config", (void **)&sub_config); ret = pmemkv_config_get_string(sub_config, "path", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERT(std::string(value_string) == "/my/path"); ret = pmemkv_config_get_int64(sub_config, "size", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_OK); UT_ASSERTeq(value_int, 1024000000); /* expect errors - wrong types */ ret = pmemkv_config_get_int64(config, "string", &value_int); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); ret = pmemkv_config_get_uint64(config, "sub_config", &value_uint); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); ret = pmemkv_config_get_string(config, "bool_f", &value_string); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); ret = pmemkv_config_get_object(config, "bool_f", (void **)&sub_config); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_TYPE_ERROR); pmemkv_config_delete(config); } static void double_test() { /** * TEST: floating point numbers are not supported */ auto config = pmemkv_config_new(); UT_ASSERT(config != nullptr); auto ret = pmemkv_config_from_json(config, "{\"double\": 12.34}"); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); UT_ASSERT( std::string(pmemkv_config_from_json_errormsg()) == "[pmemkv_config_from_json] Unsupported data type in JSON string: Number"); pmemkv_config_delete(config); } static void malformed_input_test() { /** * TEST: improperly formatted/malformed json string should return an error */ auto config = pmemkv_config_new(); UT_ASSERT(config != nullptr); auto ret = pmemkv_config_from_json(config, "{\"int\": 12"); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); UT_ASSERT(std::string(pmemkv_config_from_json_errormsg()) == "[pmemkv_config_from_json] Config parsing failed"); pmemkv_config_delete(config); } static void null_json_test() { auto config = pmemkv_config_new(); UT_ASSERT(config != nullptr); auto ret = pmemkv_config_from_json(config, nullptr); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); UT_ASSERT(std::string(pmemkv_config_from_json_errormsg()) == "[pmemkv_config_from_json] Configuration json has to be specified"); pmemkv_config_delete(config); } static void null_config_json_test() { auto ret = pmemkv_config_from_json(nullptr, nullptr); UT_ASSERTeq(ret, PMEMKV_STATUS_CONFIG_PARSING_ERROR); UT_ASSERT(std::string(pmemkv_config_from_json_errormsg()) == "[pmemkv_config_from_json] Config has to be specified"); } static void test(int argc, char *argv[]) { simple_test(); double_test(); malformed_input_test(); null_json_test(); null_config_json_test(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/ctest_helpers.cmake000066400000000000000000000307501410000423300200140ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2021, Intel Corporation # # ctest_helpers.cmake - helper functions for building and adding # new testcases (in tests/CMakeLists.txt) # set(TEST_ROOT_DIR ${PROJECT_SOURCE_DIR}/tests) set(GLOBAL_TEST_ARGS -DLIBPMEMOBJ++_LIBRARY_DIRS=${LIBPMEMOBJ++_LIBRARY_DIRS} -DPERL_EXECUTABLE=${PERL_EXECUTABLE} -DMATCH_SCRIPT=${PROJECT_SOURCE_DIR}/tests/match -DPARENT_DIR=${TEST_DIR} -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} -DTEST_ROOT_DIR=${TEST_ROOT_DIR}) if(TRACE_TESTS) set(GLOBAL_TEST_ARGS ${GLOBAL_TEST_ARGS} --trace-expand) endif() # List of supported Valgrind tracers set(vg_tracers memcheck helgrind drd pmemcheck) # ----------------------------------------------------------------- # ## Include and link required dirs/libs for tests # ----------------------------------------------------------------- # set(INCLUDE_DIRS ${LIBPMEMOBJ++_INCLUDE_DIRS} common/ ../src .) set(LIBS_DIRS ${LIBPMEMOBJ++_LIBRARY_DIRS}) include_directories(${INCLUDE_DIRS}) link_directories(${LIBS_DIRS}) # ----------------------------------------------------------------- # ## Define functions to use in tests/CMakeLists.txt # ----------------------------------------------------------------- # function(find_gdb) execute_process(COMMAND gdb --help RESULT_VARIABLE GDB_RET OUTPUT_QUIET ERROR_QUIET) if(GDB_RET) set(GDB_FOUND 0 CACHE INTERNAL "") message(WARNING "gdb not found, some tests will be skipped") else() set(GDB_FOUND 1 CACHE INTERNAL "") message(STATUS "Found gdb") endif() endfunction() function(find_pmemcheck) if(WIN32) return() endif() if(NOT VALGRIND_FOUND) return() endif() 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 "") message(STATUS "Valgrind pmemcheck found, version: ${PMEMCHECK_VERSION}") else() message(WARNING "Valgrind pmemcheck not found. Pmemcheck tests will not be performed.") endif() endfunction() function(find_libunwind) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBUNWIND QUIET libunwind) else() find_package(LIBUNWIND QUIET) endif() if(NOT LIBUNWIND_FOUND) message(WARNING "libunwind not found. Stack traces from tests will not be reliable") else() message(STATUS "Found libunwind, version ${LIBUNWIND_VERSION}") endif() endfunction() function(find_pmreorder) if(NOT VALGRIND_FOUND OR NOT VALGRIND_PMEMCHECK_FOUND) message(WARNING "Pmreorder will not be used. Valgrind with pmemcheck must be installed") return() endif() if((NOT(PMEMCHECK_VERSION VERSION_LESS 1.0)) AND PMEMCHECK_VERSION VERSION_LESS 2.0) find_program(PMREORDER names pmreorder HINTS ${LIBPMEMOBJ_PREFIX}/bin) if(PMREORDER) get_program_version_major_minor(${PMREORDER} PMREORDER_VERSION) message(STATUS "Found pmreorder: ${PMREORDER}, in version: ${PMREORDER_VERSION}") set(ENV{PATH} ${LIBPMEMOBJ_PREFIX}/bin:$ENV{PATH}) set(PMREORDER_SUPPORTED true CACHE INTERNAL "pmreorder support") else() message(WARNING "Pmreorder not found - pmreorder tests will not be performed.") endif() else() message(WARNING "Pmemcheck must be installed in version 1.X for pmreorder to work - pmreorder tests will not be performed.") endif() endfunction() function(find_pmempool) find_program(PMEMPOOL names pmempool HINTS ${LIBPMEMOBJ_PREFIX}/bin) if(PMEMPOOL) set(ENV{PATH} ${LIBPMEMOBJ_PREFIX}/bin:$ENV{PATH}) message(STATUS "Found pmempool: ${PMEMPOOL}") else() message(FATAL_ERROR "Pmempool not found.") endif() endfunction() # Function to build test with custom build options (e.g. passing defines), # link it with custom library/-ies and compile options. It calls build_test function. # Usage: build_test_ext(NAME .. SRC_FILES .. .. LIBS .. .. BUILD_OPTIONS .. .. OPTS .. ..) function(build_test_ext) set(oneValueArgs NAME) set(multiValueArgs SRC_FILES LIBS BUILD_OPTIONS OPTS) cmake_parse_arguments(TEST "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(LIBS_TO_LINK "") if(${TEST_NAME} MATCHES "posix$" AND WIN32) return() endif() foreach(lib ${TEST_LIBS}) if("${lib}" STREQUAL "json") if(NOT BUILD_JSON_CONFIG) return() else() list(APPEND LIBS_TO_LINK pmemkv_json_config) list(APPEND TEST_BUILD_OPTIONS -DJSON_TESTS_SUPPORT) endif() elseif("${lib}" STREQUAL "libpmemobj_cpp") if("${LIBPMEMOBJ++_LIBRARIES}" STREQUAL "") return() else() list(APPEND LIBS_TO_LINK ${LIBPMEMOBJ++_LIBRARIES}) endif() elseif("${lib}" STREQUAL "dl_libs") list(APPEND LIBS_TO_LINK ${CMAKE_DL_LIBS}) elseif("${lib}" STREQUAL "memkind") list(APPEND LIBS_TO_LINK ${MEMKIND_LIBRARIES}) endif() endforeach() build_test(${TEST_NAME} ${TEST_SRC_FILES}) target_link_libraries(${TEST_NAME} ${LIBS_TO_LINK}) target_compile_definitions(${TEST_NAME} PRIVATE ${TEST_BUILD_OPTIONS}) target_compile_options(${TEST_NAME} PRIVATE ${TEST_OPTS}) endfunction() function(build_test name) if(${name} MATCHES "posix$" AND WIN32) return() endif() set(srcs ${ARGN}) prepend(srcs ${CMAKE_CURRENT_SOURCE_DIR} ${srcs}) add_executable(${name} ${srcs}) target_link_libraries(${name} ${LIBPMEMOBJ_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} pmemkv test_backtrace) if(LIBUNWIND_FOUND) target_link_libraries(${name} ${LIBUNWIND_LIBRARIES} ${CMAKE_DL_LIBS}) endif() if(WIN32) target_link_libraries(${name} dbghelp) endif() add_dependencies(tests ${name}) endfunction() # Configures testcase ${test_name}_${testcase} with ${tracer} # and cmake_script used to execute test function(add_testcase executable test_name tracer testcase cmake_script) add_test(NAME ${test_name}_${testcase}_${tracer} COMMAND ${CMAKE_COMMAND} ${GLOBAL_TEST_ARGS} -DTEST_NAME=${test_name}_${testcase}_${tracer} -DTESTCASE=${testcase} -DPARENT_SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBIN_DIR=${CMAKE_CURRENT_BINARY_DIR}/${test_name}_${testcase}_${tracer} -DTEST_EXECUTABLE=$ -DTRACER=${tracer} -DLONG_TESTS=${LONG_TESTS} ${ARGN} -P ${cmake_script}) set_tests_properties(${test_name}_${testcase}_${tracer} PROPERTIES ENVIRONMENT "LC_ALL=C;PATH=$ENV{PATH};" FAIL_REGULAR_EXPRESSION Sanitizer) # XXX: if we use FATAL_ERROR in test.cmake - pmemcheck passes anyway # workaround: look for "CMake Error" in output and fail if found if (${tracer} STREQUAL pmemcheck) set_tests_properties(${test_name}_${testcase}_${tracer} PROPERTIES FAIL_REGULAR_EXPRESSION "CMake Error") endif() if (${tracer} STREQUAL pmemcheck) set_tests_properties(${test_name}_${testcase}_${tracer} PROPERTIES COST 100) elseif(${tracer} IN_LIST vg_tracers) set_tests_properties(${test_name}_${testcase}_${tracer} PROPERTIES COST 50) else() set_tests_properties(${test_name}_${testcase}_${tracer} PROPERTIES COST 10) endif() endfunction() function(skip_test name message) add_test(NAME ${name}_${message} COMMAND ${CMAKE_COMMAND} -P ${TEST_ROOT_DIR}/true.cmake) set_tests_properties(${name}_${message} PROPERTIES COST 0) endfunction() # adds testcase only if tracer is found and target is build, skips otherwise function(add_test_common executable test_name tracer testcase cmake_script) if(${tracer} STREQUAL "") set(tracer none) endif() if (NOT WIN32 AND ((NOT VALGRIND_FOUND) OR (NOT TESTS_USE_VALGRIND)) AND ${tracer} IN_LIST vg_tracers) # Only print "SKIPPED_*" message when option is enabled if (TESTS_USE_VALGRIND) skip_test(${test_name}_${testcase}_${tracer} "SKIPPED_BECAUSE_OF_MISSING_VALGRIND") endif() return() endif() if (NOT WIN32 AND ((NOT VALGRIND_PMEMCHECK_FOUND) OR (NOT TESTS_USE_VALGRIND)) AND ${tracer} STREQUAL "pmemcheck") # Only print "SKIPPED_*" message when option is enabled if (TESTS_USE_VALGRIND) skip_test(${test_name}_${testcase}_${tracer} "SKIPPED_BECAUSE_OF_MISSING_PMEMCHECK") endif() return() endif() if (NOT WIN32 AND (USE_ASAN OR USE_UBSAN) AND ${tracer} IN_LIST vg_tracers) skip_test(${test_name}_${testcase}_${tracer} "SKIPPED_BECAUSE_SANITIZER_USED") return() endif() # if test was not build if (NOT TARGET ${executable}) message(WARNING "${executable} not build. Skipping.") return() endif() # skip all valgrind tests on windows if ((NOT ${tracer} STREQUAL none) AND WIN32) return() endif() if (COVERAGE AND ${tracer} IN_LIST vg_tracers) return() endif() add_testcase(${executable} ${test_name} ${tracer} ${testcase} ${cmake_script} ${ARGN}) endfunction() # adds testcase with optional SCRIPT and TEST_CASE parameters function(add_test_generic) set(oneValueArgs NAME CASE SCRIPT) set(multiValueArgs TRACERS) cmake_parse_arguments(TEST "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if("${TEST_SCRIPT}" STREQUAL "") if("${TEST_CASE}" STREQUAL "") set(TEST_CASE "0") set(cmake_script ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run_default.cmake) else() set(cmake_script ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}/${TEST_NAME}_${TEST_CASE}.cmake) endif() else() if("${TEST_CASE}" STREQUAL "") set(TEST_CASE "0") endif() set(cmake_script ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SCRIPT}) endif() foreach(tracer ${TEST_TRACERS}) add_test_common(${TEST_NAME} ${TEST_NAME} ${tracer} ${TEST_CASE} ${cmake_script}) endforeach() endfunction() # adds testcase with additional parameters, required by "engine scenario" tests # EXTRA_CONFIG_PARAMS should be supplied in form of a json-like list, e.g. {"path":"/path/to/file"} function(add_engine_test) set(oneValueArgs BINARY ENGINE SCRIPT DB_SIZE) set(multiValueArgs TRACERS PARAMS EXTRA_CONFIG_PARAMS) cmake_parse_arguments(TEST "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(cmake_script ${CMAKE_CURRENT_SOURCE_DIR}/engines/${TEST_SCRIPT}) if("${TEST_DB_SIZE}" STREQUAL "") set(TEST_DB_SIZE 104857600) # 100MB elseif("${TEST_DB_SIZE}" STREQUAL "MIN_JEMALLOC_ARENA_SIZE") execute_process(COMMAND nproc OUTPUT_VARIABLE NPROC OUTPUT_STRIP_TRAILING_WHITESPACE) # By default jemalloc creates 4 arenas for each logical CPU with 2MB chunks math(EXPR TEST_DB_SIZE "${NPROC} * 4 * 2 * 1024 * 1024") # Limit size of the test database with arbitrary threshold: almost 2GB # (value for nproc=256 minus 2 bytes). # Can be overwritten in CMake cache using build param: -DTEST_DB_SIZE_THRESHOLD= set(TEST_DB_SIZE_THRESHOLD 2147483646 CACHE STRING "Limit calculated size of the TEST_DB_SIZE for some of the memkind tests") if(${TEST_DB_SIZE} GREATER ${TEST_DB_SIZE_THRESHOLD}) message(WARNING "Calculated TEST_DB_SIZE (${TEST_DB_SIZE}) reached threshold: ${TEST_DB_SIZE_THRESHOLD}. " "This may cause failures in some tests of memkind based engines.") set(TEST_DB_SIZE ${TEST_DB_SIZE_THRESHOLD}) endif() # Set minimum arbitrary test database to avoid test failures; this should never happen set(TEST_DB_SIZE_MIN 8388608) # 8 MB if(${TEST_DB_SIZE} LESS ${TEST_DB_SIZE_MIN}) message(FATAL_ERROR "DB_SIZE (${TEST_DB_SIZE}) was calculated below the minimum: (${TEST_DB_SIZE_MIN}).") endif() endif() get_filename_component(script_name ${cmake_script} NAME) set(parsed_script_name ${script_name}) string(REGEX REPLACE ".cmake" "" parsed_script_name ${parsed_script_name}) set(TEST_NAME "${TEST_ENGINE}__${TEST_BINARY}__${parsed_script_name}") if(NOT "${TEST_PARAMS}" STREQUAL "") string(REPLACE ";" "_" parsed_params "${TEST_PARAMS}") set(TEST_NAME "${TEST_NAME}_${parsed_params}") endif() if (NOT ("${TEST_EXTRA_CONFIG_PARAMS}" STREQUAL "")) # Parse TEST_EXTRA_CONFIG_PARAMS so it can be used in test name string(REPLACE " " "" extra_config_params ${TEST_EXTRA_CONFIG_PARAMS}) string(REPLACE ":" "_" parsed_extra_config_params ${extra_config_params}) string(REPLACE "\"" "" parsed_extra_config_params ${parsed_extra_config_params}) set(TEST_NAME "${TEST_NAME}__${parsed_extra_config_params}" CACHE INTERNAL "") endif() # Use "|PARAM|" as list separator so that CMake does not expand it # when passing to the test script string(REPLACE ";" "|PARAM|" raw_params "${TEST_PARAMS}") foreach(tracer ${TEST_TRACERS}) add_test_common(${TEST_BINARY} ${TEST_NAME} ${tracer} 0 ${cmake_script} -DENGINE=${TEST_ENGINE} -DDB_SIZE=${TEST_DB_SIZE} -DRAW_PARAMS=${raw_params} -DEXTRA_CONFIG_PARAMS=${extra_config_params}) endforeach() endfunction() pmemkv-1.5.0/tests/drd.supp000066400000000000000000000006511410000423300156250ustar00rootroot00000000000000{ Conditional variable destruction false-positive drd:CondErr ... fun:pthread_cond_destroy@* ... } { Pthread mutex lock false positive Drd:Race ... fun:pthread_mutex_*lock* ... } { Pthread rwlock lock false positive Drd:Race ... fun:pthread_rwlock_*lock* ... } { Dl lookup drd:ConflictingAccess fun:_dl_lookup_symbol_x fun:_dl_fixup fun:_dl_runtime_resolve_xsave } pmemkv-1.5.0/tests/engine_scenarios/000077500000000000000000000000001410000423300174545ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/all/000077500000000000000000000000001410000423300202245ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/all/error_handling_oom.cc000066400000000000000000000017541410000423300244110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" static void OOM(pmem::kv::db &kv) { size_t cnt = 0; while (1) { auto s = kv.put(entry_from_number(cnt), entry_from_string(std::string(cnt + 1, 'a'))); if (s == pmem::kv::status::OUT_OF_MEMORY) break; ASSERT_STATUS(s, pmem::kv::status::OK); cnt++; } /* At least one iteration */ UT_ASSERT(cnt > 0); /* Start freeing elements from the smallest one */ for (size_t i = 0; i < cnt; i++) { auto s = kv.remove(entry_from_number(i)); ASSERT_STATUS(s, pmem::kv::status::OK); } size_t count = std::numeric_limits::max(); auto s = kv.count_all(count); ASSERT_STATUS(s, pmem::kv::status::OK); UT_ASSERTeq(count, 0); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], {OOM}); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/iterate.cc000066400000000000000000000046371410000423300222020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "unittest.hpp" #include #include /** * Tests get_all and count_all methods for unsorted engines. * Since we cannot assume any order, custom sort is applied to results before comparing. */ /* XXX: are we missing check for unsorted engines, if get/count_* methods are not * supported...? */ /* XXX: this test should be extended with more data and some removal */ using namespace pmem::kv; using test_kv = std::pair; using test_kv_list = std::vector; static test_kv_list sort(test_kv_list list) { std::sort(list.begin(), list.end(), [](const test_kv &lhs, const test_kv &rhs) { return lhs.first < rhs.first; }); return list; } static void GetAllTest(pmem::kv::db &kv) { /** * TEST: get_all should return all elements in db and count_all should count them * properly */ auto entries = test_kv_list{ {entry_from_string("1"), entry_from_string("one")}, {entry_from_string("2"), entry_from_string("two")}, {entry_from_string("记!"), entry_from_string("RR")}, }; for (size_t i = 0; i < entries.size(); i++) { auto e = entries[i]; ASSERT_STATUS(kv.put(e.first, e.second), status::OK); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, i + 1); } test_kv_list result; /* get_all using string_view */ auto s = kv.get_all([&](string_view k, string_view v) { result.emplace_back(std::string(k.data(), k.size()), std::string(v.data(), v.size())); return 0; }); ASSERT_STATUS(s, status::OK); UT_ASSERT((sort(result) == sort(entries))); /* get_all with non-zero exit status from callback*/ s = kv.get_all([&](string_view k, string_view v) { return 1; }); ASSERT_STATUS(s, status::STOPPED_BY_CB); result = {}; /* get_all with C-like API */ s = kv.get_all( [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { const auto c = ((test_kv_list *)arg); c->emplace_back(std::string(k, kb), std::string(v, vb)); return 0; }, &result); ASSERT_STATUS(s, status::OK); UT_ASSERT((sort(result) == sort(entries))); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], {GetAllTest}); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/iterator_basic.cc000066400000000000000000000074271410000423300235370ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /** * Test basic methods available in iterators (sorted and unsorted engines). */ #include #include "../iterator.hpp" template static void seek_test(pmem::kv::db &kv) { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::NOT_FOUND); }); insert_keys(kv); verify_keys(it); } /* only for non const (write) iterators */ static void write_test(pmem::kv::db &kv) { insert_keys(kv); { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'x'; /* verify that value has not changed before commit */ verify_value(it, p.second); it.commit(); /* check if value has changed */ verify_value(it, std::string(res.get_value().size(), 'x')); }); /* write only two last characters */ auto last = keys.back(); it.seek(last.first); auto res = it.write_range(last.second.size() - 2, std::numeric_limits::max()); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; it.commit(); verify_value(it, std::string(last.second.size() - 2, 'x') + std::string(2, 'a')); /* write only two first characters */ it.seek(last.first); auto res2 = it.write_range(0, 2); UT_ASSERT(res2.is_ok()); for (auto &c : res2.get_value()) c = 'b'; it.commit(); verify_value(it, std::string(2, 'b') + std::string(2, 'a')); /* write only two elements from the second position */ it.seek(last.first); auto res3 = it.write_range(1, 2); UT_ASSERT(res3.is_ok()); for (auto &c : res3.get_value()) c = 'c'; it.commit(); verify_value(it, "bcca"); } /* check if a read iterator sees modifications */ auto r_it = new_iterator(kv); r_it.seek(keys.back().first); verify_value(r_it, "bcca"); } static void write_abort_test(pmem::kv::db &kv) { auto it = new_iterator(kv); insert_keys(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'x'; /* verify that value has not changed before abort */ verify_value(it, p.second); it.abort(); /* check if value has not changed after abort */ verify_value(it, p.second); }); /* check if seek will internally abort transaction */ ASSERT_STATUS(it.seek(keys.front().first), pmem::kv::status::OK); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; it.seek(keys.back().first); it.commit(); verify_keys(it); } static void zeroed_key_test(pmem::kv::db &kv) { auto element = pmem::kv::string_view("z\0z", 3); UT_ASSERTeq(element.size(), 3); auto s = kv.put(element, "val1"); ASSERT_STATUS(s, pmem::kv::status::OK); s = kv.exists(element); ASSERT_STATUS(s, pmem::kv::status::OK); auto it = new_iterator(kv); s = it.seek(element); ASSERT_STATUS(s, pmem::kv::status::OK); verify_key(it, element); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], { seek_test, seek_test, write_test, write_abort_test, zeroed_key_test, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/iterator_not_supported.cc000066400000000000000000000015141410000423300253520ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void read_iterator_not_supported(pmem::kv::db &kv) { auto res = kv.new_read_iterator(); UT_ASSERT(!res.is_ok()); auto s = res.get_status(); ASSERT_STATUS(s, status::NOT_SUPPORTED); } static void write_iterator_not_supported(pmem::kv::db &kv) { auto res = kv.new_write_iterator(); UT_ASSERT(!res.is_ok()); auto s = res.get_status(); ASSERT_STATUS(s, status::NOT_SUPPORTED); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], { write_iterator_not_supported, read_iterator_not_supported, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/open.cc000066400000000000000000000037121410000423300214770ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "unittest.hpp" /* * Tests for config flags. * Setting create_if_missing to control kv.open() or unsetting both flags * should not fail in any engine. * If engine supports these flags the scenarios below, should just open the pool. * Engines with no support for these flags should just not read them. */ static void OpenWithCreateIfMissing(std::string path, std::string engine, size_t size, bool flag_value) { /** * TEST: create_if_missing should work fine with either setting, on existing pool. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(size); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(flag_value); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); ASSERT_STATUS(s, pmem::kv::status::OK); } static void OpenWithBothFlagsFalse(std::string path, std::string engine, size_t size) { /** * TEST: both flags set to false, it should just open pool. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(size); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(false); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(false); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); ASSERT_STATUS(s, pmem::kv::status::OK); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine path size", argv[0]); auto engine = argv[1]; auto path = argv[2]; size_t size = std::stoul(argv[3]); OpenWithBothFlagsFalse(path, engine, size); for (auto flag : {true, false}) { OpenWithCreateIfMissing(path, engine, size, flag); } } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_remove.cc000066400000000000000000000321241410000423300235610ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "unittest.hpp" /** * Tests adding, reading and removing data; basic short, tests */ using namespace pmem::kv; static void SimpleTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.exists(entry_from_string("key1")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::NOT_FOUND); ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.exists(entry_from_string("key1")), status::OK); ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::OK); UT_ASSERT(value == entry_from_string("value1")); value = ""; UT_ASSERT(kv.get(entry_from_string("key1"), [&](string_view v) { value.append(v.data(), v.size()); }) == status::OK); UT_ASSERT(value == entry_from_string("value1")); } static void GetClearExternalValueTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("cool")), status::OK); std::string value = "super"; ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::OK); UT_ASSERT(value == entry_from_string("cool")); value = "super"; ASSERT_STATUS(kv.get(entry_from_string("nope"), &value), status::NOT_FOUND); UT_ASSERT(value == "super"); } static void GetHeadlessTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.exists(entry_from_string("waldo")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("waldo"), &value), status::NOT_FOUND); } static void GetMultipleTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("abc"), entry_from_string("A1")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("def"), entry_from_string("B2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("hij"), entry_from_string("C3")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("jkl"), entry_from_string("D4")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("mno"), entry_from_string("E5")), status::OK); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, 5); ASSERT_STATUS(kv.exists(entry_from_string("abc")), status::OK); std::string value1; ASSERT_STATUS(kv.get(entry_from_string("abc"), &value1), status::OK); UT_ASSERT(value1 == entry_from_string("A1")); ASSERT_STATUS(kv.exists(entry_from_string("def")), status::OK); std::string value2; ASSERT_STATUS(kv.get(entry_from_string("def"), &value2), status::OK); UT_ASSERT(value2 == entry_from_string("B2")); ASSERT_STATUS(kv.exists(entry_from_string("hij")), status::OK); std::string value3; ASSERT_STATUS(kv.get(entry_from_string("hij"), &value3), status::OK); UT_ASSERT(value3 == entry_from_string("C3")); ASSERT_STATUS(kv.exists(entry_from_string("jkl")), status::OK); std::string value4; ASSERT_STATUS(kv.get(entry_from_string("jkl"), &value4), status::OK); UT_ASSERT(value4 == entry_from_string("D4")); ASSERT_STATUS(kv.exists(entry_from_string("mno")), status::OK); std::string value5; ASSERT_STATUS(kv.get(entry_from_string("mno"), &value5), status::OK); UT_ASSERT(value5 == entry_from_string("E5")); } static void GetMultiple2Test(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key2"), entry_from_string("value2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key3"), entry_from_string("value3")), status::OK); ASSERT_STATUS(kv.remove(entry_from_string("key2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key3"), entry_from_string("VALUE3")), status::OK); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); std::string value1; ASSERT_STATUS(kv.get(entry_from_string("key1"), &value1), status::OK); UT_ASSERT(value1 == entry_from_string("value1")); std::string value2; ASSERT_STATUS(kv.get(entry_from_string("key2"), &value2), status::NOT_FOUND); std::string value3; ASSERT_STATUS(kv.get(entry_from_string("key3"), &value3), status::OK); UT_ASSERT(value3 == entry_from_string("VALUE3")); } static void GetNonexistentTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); ASSERT_STATUS(kv.exists(entry_from_string("waldo")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("waldo"), &value), status::NOT_FOUND); } static void PutTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); std::string value; ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::OK); UT_ASSERT(value == entry_from_string("value1")); std::string new_value; ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("VALUE1")), status::OK); // same size cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get(entry_from_string("key1"), &new_value), status::OK); UT_ASSERT(new_value == entry_from_string("VALUE1")); std::string new_value2; ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("new_val")), status::OK); // longer size cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get(entry_from_string("key1"), &new_value2), status::OK); UT_ASSERT(new_value2 == entry_from_string("new_val")); std::string new_value3; ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("?")), status::OK); // shorter size cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get(entry_from_string("key1"), &new_value3), status::OK); UT_ASSERT(new_value3 == entry_from_string("?")); } static void RemoveAllTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.put(entry_from_string("tmpkey"), entry_from_string("tmpval1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.remove(entry_from_string("tmpkey")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("tmpkey"), &value), status::NOT_FOUND); } static void RemoveAndInsertTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.put(entry_from_string("tmpkey"), entry_from_string("tmpval1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.remove(entry_from_string("tmpkey")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("tmpkey"), &value), status::NOT_FOUND); ASSERT_STATUS(kv.put(entry_from_string("tmpkey1"), entry_from_string("tmpval1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey1")), status::OK); ASSERT_STATUS(kv.get(entry_from_string("tmpkey1"), &value), status::OK); UT_ASSERT(value == entry_from_string("tmpval1")); ASSERT_STATUS(kv.remove(entry_from_string("tmpkey1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey1")), status::NOT_FOUND); ASSERT_STATUS(kv.get(entry_from_string("tmpkey1"), &value), status::NOT_FOUND); } static void RemoveExistingTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.put(entry_from_string("tmpkey1"), entry_from_string("tmpval1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.put(entry_from_string("tmpkey2"), entry_from_string("tmpval2")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.remove(entry_from_string("tmpkey1")), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, 1); ASSERT_STATUS(kv.remove(entry_from_string("tmpkey1")), status::NOT_FOUND); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey1")), status::NOT_FOUND); std::string value; ASSERT_STATUS(kv.get(entry_from_string("tmpkey1"), &value), status::NOT_FOUND); ASSERT_STATUS(kv.exists(entry_from_string("tmpkey2")), status::OK); ASSERT_STATUS(kv.get(entry_from_string("tmpkey2"), &value), status::OK); UT_ASSERT(value == entry_from_string("tmpval2")); } static void RemoveHeadlessTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.remove(entry_from_string("nada")), status::NOT_FOUND); } static void RemoveNonexistentTest(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); ASSERT_STATUS(kv.remove(entry_from_string("nada")), status::NOT_FOUND); ASSERT_STATUS(kv.exists(entry_from_string("key1")), status::OK); } static void ZeroFilledStringTest(pmem::kv::db &kv) { uint64_t z = 0; std::string value; auto zero_filled_str = uint64_to_string(z); ASSERT_STATUS(kv.get(entry_from_string(zero_filled_str), &value), status::NOT_FOUND); ASSERT_STATUS(kv.put(entry_from_string(zero_filled_str), entry_from_string(zero_filled_str)), status::OK); ASSERT_STATUS(kv.count_all(z), status::OK); UT_ASSERT(z == 1); ASSERT_STATUS(kv.get(entry_from_string(zero_filled_str), &value), status::OK); UT_ASSERT(value == entry_from_string(zero_filled_str)); ASSERT_STATUS(kv.remove(entry_from_string(zero_filled_str)), status::OK); ASSERT_STATUS(kv.count_all(z), status::OK); UT_ASSERT(z == 0); } static void MoveDBTest(pmem::kv::db &kv) { /** * TEST: test db constructor from another instance of db class * and move assignment operator (from different and the same db). */ /* put key1 in original db */ ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); db kv_new(std::move(kv)); ASSERT_STATUS(kv_new.put(entry_from_string("key2"), entry_from_string("value2")), status::OK); std::string value; value = "ABC"; ASSERT_STATUS(kv_new.get(entry_from_string("key1"), &value), status::OK); UT_ASSERT(value == entry_from_string("value1")); ASSERT_STATUS(kv_new.get(entry_from_string("key2"), &value), status::OK); UT_ASSERT(value == entry_from_string("value2")); ASSERT_STATUS(kv_new.remove(entry_from_string("key1")), status::OK); db kv_assign_new = std::move(kv_new); auto &kv_assign_new2 = kv_assign_new; kv_assign_new = std::move(kv_assign_new2); ASSERT_STATUS( kv_assign_new.put(entry_from_string("key3"), entry_from_string("value3")), status::OK); ASSERT_STATUS(kv_assign_new.get(entry_from_string("key2"), &value), status::OK); UT_ASSERT(value == entry_from_string("value2")); ASSERT_STATUS(kv_assign_new.get(entry_from_string("key3"), &value), status::OK); UT_ASSERT(value == entry_from_string("value3")); ASSERT_STATUS(kv_assign_new.remove(entry_from_string("key2")), status::OK); ASSERT_STATUS(kv_assign_new.remove(entry_from_string("key3")), status::OK); kv_assign_new.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests( argv[1], argv[2], { SimpleTest, GetClearExternalValueTest, GetHeadlessTest, GetMultipleTest, GetMultiple2Test, GetNonexistentTest, PutTest, RemoveAllTest, RemoveAndInsertTest, RemoveExistingTest, RemoveHeadlessTest, RemoveNonexistentTest, ZeroFilledStringTest, /* move DB test has to be the last one; it invalidates kv */ MoveDBTest, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_remove_charset_params.cc000066400000000000000000000207201410000423300266340ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include #include #include using namespace pmem::kv; static const std::string PREFIX = "in"; static const std::string SUFFIX = ";"; static const std::string CLEAN_KEY_SUFFIX = "_cl"; static constexpr size_t CHARSET_RANGE_START = 0; static constexpr size_t CHARSET_RANGE_END = (size_t)UCHAR_MAX; static constexpr size_t CHARSET_LEN = CHARSET_RANGE_END - CHARSET_RANGE_START + 1; /* It generates a set that contains random strings of various length but no longer than * 'max_str_len'. */ std::set generate_binary_strings(const size_t cnt, const size_t max_str_len) { std::set strings; size_t n = 0; do { std::string gen_str = std::to_string(n); /* various length of string, min: len of gen_str */ size_t rand_len = (size_t)rand() % (max_str_len - gen_str.size()); for (size_t i = 0; i < rand_len; i++) { gen_str.push_back( char(CHARSET_RANGE_START + (size_t)rand() % CHARSET_LEN)); } if (strings.insert(gen_str).second) ++n; } while (n != cnt); return strings; } static void BinaryKeyTest(pmem::kv::db &kv) { /** * TEST: each char from the char range is used in two keys * once with prefix and suffix, once just as is ("clean key") */ size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); auto key = entry_from_string(PREFIX); auto value = entry_from_string("constval"); ASSERT_STATUS(kv.exists(key), status::NOT_FOUND); ASSERT_STATUS(kv.put(key, value), status::OK); ASSERT_STATUS(kv.exists(key), status::OK); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); /* Add binary keys */ for (auto i = CHARSET_RANGE_START; i <= CHARSET_RANGE_END; i++) { /* key with prefix and suffix */ std::string key1 = entry_from_string(std::string(PREFIX + char(i) + SUFFIX)); ASSERT_STATUS(kv.exists(key1), status::NOT_FOUND); ASSERT_STATUS(kv.put(key1, entry_from_number(i)), status::OK); /* "clean" key */ std::string key2 = entry_from_string(std::string(1, char(i))); ASSERT_STATUS(kv.exists(key2), status::NOT_FOUND); ASSERT_STATUS(kv.put(key2, entry_from_number(i)), status::OK); } std::string val; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == (CHARSET_LEN * 2 + 1)); ASSERT_STATUS(kv.get(key, &val), status::OK); UT_ASSERT(val == value); /* Read and remove binary keys */ for (auto i = CHARSET_RANGE_START; i <= CHARSET_RANGE_END; i++) { /* key with prefix and suffix */ std::string key1 = entry_from_string(std::string(PREFIX + char(i) + SUFFIX)); ASSERT_STATUS(kv.exists(key1), status::OK); ASSERT_STATUS(kv.get(key1, &val), status::OK); UT_ASSERT(val == entry_from_number(i)); ASSERT_STATUS(kv.remove(key1), status::OK); ASSERT_STATUS(kv.exists(key1), status::NOT_FOUND); /* "clean" key */ std::string key2 = entry_from_string(std::string(1, char(i))); ASSERT_STATUS(kv.exists(key2), status::OK); ASSERT_STATUS(kv.get(key2, &val), status::OK); UT_ASSERT(val == entry_from_number(i)); ASSERT_STATUS(kv.remove(key2), status::OK); ASSERT_STATUS(kv.exists(key2), status::NOT_FOUND); } ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get(key, &val), status::OK); UT_ASSERT(val == value); ASSERT_STATUS(kv.remove(key), status::OK); } static void BinaryRandKeyTest(const size_t elements_cnt, const size_t max_key_len, pmem::kv::db &kv) { /** * TEST: keys are randomly generated from the full range of the charset */ size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); auto keys = generate_binary_strings(elements_cnt, max_key_len); /* Add elements with generated keys */ size_t i = 0; for (auto &str : keys) { std::string istr = entry_from_number(i++); std::string key = entry_from_string(str); ASSERT_STATUS(kv.put(key, istr), status::OK); } std::string value; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == elements_cnt); /* Read and remove elements with generated keys (in reverse order) */ i = cnt; for (auto it = keys.rbegin(); it != keys.rend(); ++it) { std::string istr = entry_from_number(--i); std::string key = entry_from_string(*it); ASSERT_STATUS(kv.exists(key), status::OK); ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == istr); ASSERT_STATUS(kv.remove(key), status::OK); ASSERT_STATUS(kv.exists(key), status::NOT_FOUND); } ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); } static void BinaryValueTest(pmem::kv::db &kv) { /** * TEST: each char from the char range is used as value twice - * once with prefix and suffix, once just as is ("clean value") */ size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); /* Add elements with binary values */ for (auto i = CHARSET_RANGE_START; i <= CHARSET_RANGE_END; i++) { /* value with prefix and suffix */ std::string key1 = entry_from_number(i); std::string value1 = entry_from_string(std::string(PREFIX + char(i) + SUFFIX)); ASSERT_STATUS(kv.exists(key1), status::NOT_FOUND); ASSERT_STATUS(kv.put(key1, value1), status::OK); /* "clean" value */ std::string key2 = entry_from_number(i, "", CLEAN_KEY_SUFFIX); std::string value2 = entry_from_string(std::string(1, char(i))); ASSERT_STATUS(kv.exists(key2), status::NOT_FOUND); ASSERT_STATUS(kv.put(key2, value2), status::OK); } std::string value; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == (CHARSET_LEN * 2)); /* Read and remove elements with binary values */ for (auto i = CHARSET_RANGE_START; i <= CHARSET_RANGE_END; i++) { /* value with prefix and suffix */ std::string key1 = entry_from_number(i); std::string value1 = entry_from_string(std::string(PREFIX + char(i) + SUFFIX)); ASSERT_STATUS(kv.exists(key1), status::OK); ASSERT_STATUS(kv.get(key1, &value), status::OK); UT_ASSERT(value == value1); UT_ASSERTeq(value.length(), value1.length()); ASSERT_STATUS(kv.remove(key1), status::OK); ASSERT_STATUS(kv.exists(key1), status::NOT_FOUND); /* "clean" value */ std::string key2 = entry_from_number(i, "", CLEAN_KEY_SUFFIX); std::string value2 = entry_from_string(std::string(1, char(i))); ASSERT_STATUS(kv.exists(key2), status::OK); ASSERT_STATUS(kv.get(key2, &value), status::OK); UT_ASSERT(value == value2); UT_ASSERTeq(value.length(), value2.length()); ASSERT_STATUS(kv.remove(key2), status::OK); ASSERT_STATUS(kv.exists(key2), status::NOT_FOUND); } ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); } static void BinaryRandValueTest(const size_t elements_cnt, const size_t max_value_len, pmem::kv::db &kv) { /** * TEST: values are randomly generated from the full range of the charset */ size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); auto values = generate_binary_strings(elements_cnt, max_value_len); /* Add elements with generated values */ size_t i = 0; for (auto &str : values) { std::string key = entry_from_number(i++); std::string value = entry_from_string(str); ASSERT_STATUS(kv.put(key, value), status::OK); } std::string value; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == elements_cnt); /* Read and remove elements with generated values (in reverse order) */ i = cnt; for (auto it = values.rbegin(); it != values.rend(); ++it) { std::string key = entry_from_number(--i); std::string exp_value = entry_from_string(*it); ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == exp_value); UT_ASSERTeq(value.length(), exp_value.length()); ASSERT_STATUS(kv.remove(key), status::OK); } ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 5) UT_FATAL("usage: %s engine json_config elements_cnt max_str_len", argv[0]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); auto elements_cnt = std::stoull(argv[3]); auto max_str_len = std::stoull(argv[4]); run_engine_tests( argv[1], argv[2], { BinaryKeyTest, std::bind(BinaryRandKeyTest, elements_cnt, max_str_len, _1), BinaryValueTest, std::bind(BinaryRandValueTest, elements_cnt, max_str_len, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_remove_long_key.cc000066400000000000000000000036151410000423300254530ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2020, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void PutKeysOfDifferentSizesTest(pmem::kv::db &kv) { std::string value; ASSERT_STATUS(kv.put("123456789ABCDE", "A"), status::OK); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get("123456789ABCDE", &value), status::OK); UT_ASSERT(value == "A"); std::string value2; ASSERT_STATUS(kv.put("123456789ABCDEF", "B"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.get("123456789ABCDEF", &value2), status::OK); UT_ASSERT(value2 == "B"); std::string value3; ASSERT_STATUS(kv.put("12345678ABCDEFG", "C"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 3); ASSERT_STATUS(kv.get("12345678ABCDEFG", &value3), status::OK); UT_ASSERT(value3 == "C"); std::string value4; ASSERT_STATUS(kv.put("123456789", "D"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 4); ASSERT_STATUS(kv.get("123456789", &value4), status::OK); UT_ASSERT(value4 == "D"); std::string value5; ASSERT_STATUS(kv.put("123456789ABCDEFGHI", "E"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 5); ASSERT_STATUS(kv.get("123456789ABCDEFGHI", &value5), status::OK); UT_ASSERT(value5 == "E"); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], { PutKeysOfDifferentSizesTest, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_remove_not_aligned.cc000066400000000000000000000112731410000423300261260ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "unittest.hpp" /** * Tests adding, reading and removing data; basic short, tests. Only for engines with no * fixed-size keys, because we testing there empty keys, keys with various size etc. */ using namespace pmem::kv; static void EmptyKeyTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.put("", "empty"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.put(" ", "1-space"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.put("\t\t", "two-tab"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_STATUS(kv.exists(""), status::OK); ASSERT_STATUS(kv.get("", &value1), status::OK); UT_ASSERT(value1 == "empty"); ASSERT_STATUS(kv.exists(" "), status::OK); ASSERT_STATUS(kv.get(" ", &value2), status::OK); UT_ASSERT(value2 == "1-space"); ASSERT_STATUS(kv.exists("\t\t"), status::OK); ASSERT_STATUS(kv.get("\t\t", &value3), status::OK); UT_ASSERT(value3 == "two-tab"); } static void EmptyValueTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.put("empty", ""), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.put("1-space", " "), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.put("two-tab", "\t\t"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 3); std::string value1; std::string value2; std::string value3; ASSERT_STATUS(kv.get("empty", &value1), status::OK); UT_ASSERT(value1 == ""); ASSERT_STATUS(kv.get("1-space", &value2), status::OK); UT_ASSERT(value2 == " "); ASSERT_STATUS(kv.get("two-tab", &value3), status::OK); UT_ASSERT(value3 == "\t\t"); } static void EmptyKeyAndValueTest(pmem::kv::db &kv) { std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 0); std::string value = "abc"; ASSERT_STATUS(kv.get("", &value), status::NOT_FOUND); UT_ASSERT(value == "abc"); ASSERT_STATUS(kv.put("", ""), status::OK); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get("", &value), status::OK); UT_ASSERT(value == ""); } static void PutValuesOfDifferentSizesTest(pmem::kv::db &kv) { std::string value; ASSERT_STATUS(kv.put("A", "123456789ABCDE"), status::OK); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.get("A", &value), status::OK); UT_ASSERT(value == "123456789ABCDE"); std::string value2; ASSERT_STATUS(kv.put("B", "123456789ABCDEF"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.get("B", &value2), status::OK); UT_ASSERT(value2 == "123456789ABCDEF"); std::string value3; ASSERT_STATUS(kv.put("C", "12345678ABCDEFG"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 3); ASSERT_STATUS(kv.get("C", &value3), status::OK); UT_ASSERT(value3 == "12345678ABCDEFG"); std::string value4; ASSERT_STATUS(kv.put("D", "123456789"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 4); ASSERT_STATUS(kv.get("D", &value4), status::OK); UT_ASSERT(value4 == "123456789"); std::string value5; ASSERT_STATUS(kv.put("E", "123456789ABCDEFGHI"), status::OK); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 5); ASSERT_STATUS(kv.get("E", &value5), status::OK); UT_ASSERT(value5 == "123456789ABCDEFGHI"); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], { EmptyKeyTest, EmptyValueTest, EmptyKeyAndValueTest, PutValuesOfDifferentSizesTest, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_remove_params.cc000066400000000000000000000037421410000423300251300ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void LargeAscendingTest(const size_t iterations, pmem::kv::db &kv) { for (size_t i = 1; i <= iterations; i++) { std::string istr = entry_from_number(i); ASSERT_STATUS(kv.put(istr, entry_from_number(i, "", "!")), status::OK); std::string value; ASSERT_STATUS(kv.get(istr, &value), status::OK); UT_ASSERT(value == entry_from_number(i, "", "!")); } for (size_t i = 1; i <= iterations; i++) { std::string istr = entry_from_number(i); std::string value; ASSERT_STATUS(kv.get(istr, &value), status::OK); UT_ASSERT(value == entry_from_number(i, "", "!")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == iterations); } static void LargeDescendingTest(const size_t iterations, pmem::kv::db &kv) { for (size_t i = iterations; i >= 1; i--) { std::string istr = entry_from_number(i); ASSERT_STATUS(kv.put(istr, entry_from_number(i, "ABC")), status::OK); std::string value; ASSERT_STATUS(kv.get(istr, &value), status::OK); UT_ASSERT(value == entry_from_number(i, "ABC")); } for (size_t i = iterations; i >= 1; i--) { std::string istr = entry_from_number(i); std::string value; ASSERT_STATUS(kv.get(istr, &value), status::OK); UT_ASSERT(value == entry_from_number(i, "ABC")); } std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == iterations); } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 4) UT_FATAL("usage: %s engine json_config iterations", argv[0]); auto iterations = std::stoull(argv[3]); run_engine_tests(argv[1], argv[2], { std::bind(LargeAscendingTest, iterations, _1), std::bind(LargeDescendingTest, iterations, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/all/put_get_std_map.cc000066400000000000000000000012521410000423300237110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 6) UT_FATAL("usage: %s engine json_config n_inserts key_length value_length", argv[0]); auto n_inserts = std::stoull(argv[3]); auto key_length = std::stoull(argv[4]); auto value_length = std::stoull(argv[5]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); auto proto = PutToMapTest(n_inserts, key_length, value_length, kv); VerifyKv(proto, kv); kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/concurrent/000077500000000000000000000000001410000423300216365ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/concurrent/iterate_params.cc000066400000000000000000000132361410000423300251520ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include #include #include #include #include using namespace pmem::kv; static size_t value_prefix_size = 256; static std::map t_seed; static std::mutex mtx; static std::mt19937_64 main_generator; std::mt19937_64 make_ts_generator(size_t thread_id, std::function syncthreads) { std::random_device rd; auto seed = rd(); { std::unique_lock lock(mtx); t_seed[thread_id] = seed; } std::mt19937_64 g(seed); syncthreads(); if (thread_id == 0) { for (auto i : t_seed) std::cout << "tid: " << i.first << " seed: " << i.second << std::endl; } syncthreads(); return g; } uint64_t unique_value(std::mt19937_64 &generator, std::set &set) { uint64_t v; do { v = generator(); } while (set.count(v) == 1); return v; } void verify_init_elements(const std::set &init, pmem::kv::db &kv) { std::set keys; auto s = kv.get_all([&](string_view k, string_view v) { uint64_t *uk = (uint64_t *)k.data(); std::string value1 = entry_from_string( uint64_to_string(*uk) + std::string(value_prefix_size, '0')); std::string value2 = entry_from_string( uint64_to_string(*uk) + std::string(value_prefix_size, '1')); UT_ASSERT(v.compare(value1) == 0 || v.compare(value2) == 0); keys.insert(*uk); return 0; }); ASSERT_STATUS(s, status::OK); UT_ASSERT(keys.size() >= init.size()); for (const auto &v : init) { UT_ASSERTeq(keys.count(v), 1); } } static void ConcurrentIterationAndPutTest(const size_t threads_number, const size_t thread_items, pmem::kv::db &kv) { /** * TEST: prepares thread_number * thread_items elements in pmemkv. * Then, it concurrently inserts additional element and updates existing one. * At the same time some threads are iterating over pmemkv making sure * the initial data is still accessible. */ std::set set; auto init_size = threads_number * thread_items; for (uint64_t i = 0; i < init_size; i++) { auto k = unique_value(main_generator, set); set.insert(k); std::string value = entry_from_string( uint64_to_string(k) + std::string(value_prefix_size, '0')); ASSERT_STATUS(kv.put(uint64_to_strv(k), value), status::OK); } parallel_xexec( threads_number, [&](size_t thread_id, std::function syncthreads) { auto g = make_ts_generator(thread_id, syncthreads); if (thread_id < threads_number / 4) { for (uint64_t i = 0; i < thread_items; i++) { auto k = unique_value(g, set); std::string value = entry_from_string( uint64_to_string(k) + std::string(value_prefix_size, '0')); ASSERT_STATUS(kv.put(uint64_to_strv(k), value), status::OK); } } else if (thread_id < threads_number / 2) { for (uint64_t i = 0; i < thread_items; i++) { auto existing_e = *set.find(g() % set.size()); std::string value = entry_from_string( uint64_to_string(existing_e) + std::string(value_prefix_size, '1')); ASSERT_STATUS( kv.put(uint64_to_strv(existing_e), value), status::OK); } } else { verify_init_elements(set, kv); } }); verify_init_elements(set, kv); } static void ConcurrentIterationAndRemoveTest(const size_t threads_number, const size_t thread_items, pmem::kv::db &kv) { /** * TEST: prepares 2 * thread_number * thread_items elements in pmemkv. * Then, it concurrently removes half of them, while making sure the other * half is still accessible. */ std::set init_set; std::set to_remove_set; auto init_size = threads_number * thread_items; for (uint64_t i = 0; i < init_size; i++) { auto k = unique_value(main_generator, init_set); init_set.insert(k); std::string value = entry_from_string( uint64_to_string(k) + std::string(value_prefix_size, '0')); ASSERT_STATUS(kv.put(uint64_to_strv(k), value), status::OK); } for (uint64_t i = 0; i < init_size; i++) { auto k = unique_value(main_generator, to_remove_set); to_remove_set.insert(k); std::string value = entry_from_string( uint64_to_string(k) + std::string(value_prefix_size, '0')); ASSERT_STATUS(kv.put(uint64_to_strv(k), value), status::OK); } parallel_xexec(threads_number, [&](size_t thread_id, std::function syncthreads) { auto g = make_ts_generator(thread_id, syncthreads); if (thread_id < threads_number / 2) { for (uint64_t i = 0; i < thread_items; i++) { auto existing_e = *to_remove_set.find( g() % to_remove_set.size()); auto s = kv.remove( uint64_to_strv(existing_e)); UT_ASSERT(s == status::OK || s == status::NOT_FOUND); } } else { verify_init_elements(init_set, kv); } }); verify_init_elements(init_set, kv); } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 5) UT_FATAL("usage: %s engine json_config threads items [value_prefix_size]", argv[0]); if (argc > 5) value_prefix_size = std::stoul(argv[5]); std::random_device rd; auto seed = rd(); std::cout << "main thread rand seed: " << seed << std::endl; main_generator = std::mt19937_64(seed); size_t threads_number = std::stoull(argv[3]); size_t thread_items = std::stoull(argv[4]); run_engine_tests(argv[1], argv[2], { std::bind(ConcurrentIterationAndPutTest, threads_number, thread_items, _1), std::bind(ConcurrentIterationAndRemoveTest, threads_number, thread_items, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/concurrent/iterator_concurrent.cc000066400000000000000000000135401410000423300262430ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ /** * Tests methods of iterators in the concurrent way (only iterators of concurrent * engines). */ #include "../iterator.hpp" #include static void init_keys(pmem::kv::db &kv, size_t size) { for (size_t i = 0; i < size; i++) ASSERT_STATUS(kv.put(std::to_string(i), std::string(20 + i, 'x')), pmem::kv::status::OK); } static void verify_kv(pmem::kv::db &kv, size_t size) { auto it = new_iterator(kv); for (size_t i = 0; i < size; i++) { ASSERT_STATUS(it.seek(std::to_string(i)), pmem::kv::status::OK); verify_value(it, std::string(20 + i, 'x')); } } static void concurrent_write(size_t threads_number, pmem::kv::db &kv) { const size_t n = threads_number * 10; init_keys(kv, n); parallel_exec(threads_number + 1, [&](size_t thread_id) { /* check consistency */ if (thread_id == threads_number) { auto it = new_iterator(kv); for (size_t i = 0; i < n; i++) { ASSERT_STATUS(it.seek(std::to_string(i)), pmem::kv::status::OK); auto res = it.read_range(); UT_ASSERT(res.is_ok()); auto value = std::string(res.get_value().data(), res.get_value().size()); std::string possible_values[4] = { std::string(20 + i, 'x'), std::string(10, 'b') + std::string(10 + i, 'x'), std::string(10, 'x') + std::string(10 + i, 'a'), std::string(10, 'b') + std::string(10 + i, 'a'), }; UT_ASSERT( std::any_of(possible_values, possible_values + 4, [&](std::string s) { return s.compare(value) == 0; })); } return; } auto it = new_iterator(kv); for (size_t i = thread_id / 2; i < n; i += threads_number / 2) { ASSERT_STATUS(it.seek(std::to_string(i)), pmem::kv::status::OK); /* write 'a' to chars from 10 to end */ if (thread_id % 2) { auto res = it.write_range( 10, std::numeric_limits::max()); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; } /* write 'b' to chars from begin to 10 */ else { auto res = it.write_range(0, 10); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'b'; } ASSERT_STATUS(it.commit(), pmem::kv::status::OK); } }); auto it = new_iterator(kv); for (size_t i = 0; i < n; i++) { ASSERT_STATUS(it.seek(std::to_string(i)), pmem::kv::status::OK); verify_value(it, std::string(10, 'b') + std::string(10 + i, 'a')); } } static void concurrent_write_abort(size_t threads_number, pmem::kv::db &kv) { const size_t n = threads_number * 10; init_keys(kv, n); parallel_exec(threads_number + 1, [&](size_t thread_id) { if (thread_id == threads_number) { verify_kv(kv, n); return; } auto it = new_iterator(kv); for (size_t i = thread_id / 2; i < n; i += threads_number) { it.seek(std::to_string(i)); if (thread_id % 2) { auto res = it.write_range( 10, std::numeric_limits::max()); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; } else { auto res = it.write_range(0, 10); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'b'; } it.abort(); } }); verify_kv(kv, n); } /* only for sorted engines */ static void concurrent_write_sorted(size_t threads_number, pmem::kv::db &kv) { const size_t n = threads_number * 10; init_keys(kv, n); parallel_exec(threads_number + 1, [&](size_t thread_id) { /* check consistency */ if (thread_id == threads_number) { auto it = new_iterator(kv); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); do { auto key = it.key(); UT_ASSERT(key.is_ok()); auto key_as_number = static_cast( std::stoi(key.get_value().data())); auto res = it.read_range(); UT_ASSERT(res.is_ok()); auto value = std::string(res.get_value().data(), res.get_value().size()); std::string possible_values[4] = { std::string(20 + key_as_number, 'x'), std::string(10, 'b') + std::string(10 + key_as_number, 'x'), std::string(10, 'x') + std::string(10 + key_as_number, 'a'), std::string(10, 'b') + std::string(10 + key_as_number, 'a'), }; UT_ASSERT( std::any_of(possible_values, possible_values + 4, [&](std::string s) { return s.compare(value) == 0; })); } while (it.next() == pmem::kv::status::OK); return; } auto it = new_iterator(kv); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); do { /* write 'a' to chars from 10 to end */ if (thread_id % 2) { auto res = it.write_range( 10, std::numeric_limits::max()); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; } /* write 'b' to chars from begin to 10 */ else { auto res = it.write_range(0, 10); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'b'; } ASSERT_STATUS(it.commit(), pmem::kv::status::OK); } while (it.next() == pmem::kv::status::OK); }); auto it = new_iterator(kv); for (size_t i = 0; i < n; i++) { ASSERT_STATUS(it.seek(std::to_string(i)), pmem::kv::status::OK); verify_value(it, std::string(10, 'b') + std::string(10 + i, 'a')); } } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 4) UT_FATAL("usage: %s engine json_config threads [is_sorted]", argv[0]); size_t threads_number = std::stoull(argv[3]); run_engine_tests(argv[1], argv[2], { std::bind(concurrent_write_abort, threads_number, _1), std::bind(concurrent_write, threads_number, _1), }); /* only for sorted engines */ if (argc > 4 && std::string(argv[4]).compare("true") == 0) { run_engine_tests( argv[1], argv[2], { std::bind(concurrent_write_sorted, threads_number, _1), }); } } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/concurrent/put_get_remove_gen_params.cc000066400000000000000000000106031410000423300273650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include #include /** * Tests concurrency with parallel data read and removal. Data is generated with * parametrized thread count, database elements count and max key length. */ using namespace pmem::kv; void generate_keys(std::vector &keys, const size_t max_key_len, const size_t cnt) { const char charset[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" "!@#$%^&*()_+,./<>?'\"`~;:[]{}\\|"; const size_t charset_size = sizeof(charset); std::unordered_set unique_keys; size_t k = 0; while (k < cnt) { /* various lengths of key, min: 1 */ size_t key_len = 1 + ((size_t)rand() % max_key_len); std::string key; key.reserve(key_len); for (size_t i = 0; i < key_len; i++) { key.push_back(charset[(size_t)rand() % charset_size]); } std::string generated_key = entry_from_string(key); /* if key doesn't already exists, insert */ if (unique_keys.insert(generated_key).second) ++k; } keys = std::vector(unique_keys.begin(), unique_keys.end()); } static void MultithreadedTestRemoveDataAside(const size_t threads_number, const size_t thread_items, const size_t max_key_len, pmem::kv::db &kv) { /** * TEST: reads initial data in parallel while operating on other (generated) data. */ size_t initial_count = 128; /* put initial data, which won't be modified */ for (size_t i = 0; i < initial_count; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); ASSERT_STATUS(kv.put(key, val), status::OK); } std::vector keys; auto keys_cnt = threads_number * thread_items; generate_keys(keys, max_key_len, keys_cnt); /* test parallelly adding data (+ read initial data) */ parallel_exec(threads_number + 1, [&](size_t thread_id) { if (thread_id == threads_number) { for (size_t i = 0; i < initial_count; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } return; } size_t begin = thread_id * thread_items; size_t end = begin + thread_items; for (auto i = begin; i < end; i++) { ASSERT_STATUS(kv.put(keys[i], keys[i]), status::OK); } for (auto i = begin; i < end; i++) { std::string value; ASSERT_STATUS(kv.get(keys[i], &value), status::OK); UT_ASSERT(value == keys[i]); } }); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, initial_count + keys_cnt); /* test parallelly removing data (+ read initial data) */ parallel_exec(threads_number + 1, [&](size_t thread_id) { if (thread_id == threads_number) { for (size_t i = 0; i < initial_count; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } return; } size_t begin = thread_id * thread_items; size_t end = begin + thread_items; for (auto i = begin; i < end; i++) { ASSERT_STATUS(kv.remove(keys[i]), status::OK); } }); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == initial_count); /* get initial data and confirm it's unmodified */ for (size_t i = 0; i < initial_count; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 6) UT_FATAL("usage: %s engine json_config threads items max_key_len", argv[0]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); size_t threads_number = std::stoull(argv[3]); size_t thread_items = std::stoull(argv[4]); size_t max_key_len = std::stoull(argv[5]); run_engine_tests(argv[1], argv[2], { std::bind(MultithreadedTestRemoveDataAside, threads_number, thread_items, max_key_len, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/concurrent/put_get_remove_params.cc000066400000000000000000000104261410000423300265370ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void SimpleMultithreadedTest(const size_t threads_number, const size_t thread_items, pmem::kv::db &kv) { 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 key = entry_from_number(i); std::string val = entry_from_number(i, "", "!"); ASSERT_STATUS(kv.put(key, val), status::OK); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } for (auto i = begin; i < end; i++) { std::string key = entry_from_number(i); std::string val = entry_from_number(i, "", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } }); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == threads_number * thread_items); } static void MultithreadedTestRemoveDataAside(const size_t threads_number, const size_t thread_items, pmem::kv::db &kv) { size_t initial_items = 128; /* put initial data, which won't be touched */ for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); ASSERT_STATUS(kv.put(key, val), status::OK); } /* test adding and removing data */ 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 key = entry_from_number(i); std::string val = entry_from_number(i, "", "!"); ASSERT_STATUS(kv.put(key, val), status::OK); } for (auto i = begin; i < end; i++) { std::string key = entry_from_number(i); std::string val = entry_from_number(i, "", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); ASSERT_STATUS(kv.remove(key), status::OK); } }); std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == initial_items); /* get initial data and confirm it's untouched */ for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i, "in_"); std::string val = entry_from_number(i, "in_", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == val); } } static void MultithreadedPutRemove(const size_t threads_number, const size_t thread_items, pmem::kv::db &kv) { size_t initial_items = threads_number * thread_items; for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i); std::string value = entry_from_number(i, "", "!"); ASSERT_STATUS(kv.put(key, value), status::OK); } parallel_exec(threads_number, [&](size_t thread_id) { if (thread_id < threads_number / 2) { for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i); std::string value = entry_from_number(i, "", "!"); ASSERT_STATUS(kv.put(key, value), status::OK); } } else { for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i); auto s = kv.remove(key); UT_ASSERT(s == status::OK || s == status::NOT_FOUND); } } }); for (size_t i = 0; i < initial_items; i++) { std::string key = entry_from_number(i); std::string value = entry_from_number(i, "", "!"); std::string val; auto s = kv.get(key, &val); UT_ASSERT((s == status::OK && val == value) || s == status::NOT_FOUND); } } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 5) UT_FATAL("usage: %s engine json_config threads items", argv[0]); size_t threads_number = std::stoull(argv[3]); size_t thread_items = std::stoull(argv[4]); run_engine_tests(argv[1], argv[2], { std::bind(SimpleMultithreadedTest, threads_number, thread_items, _1), std::bind(MultithreadedTestRemoveDataAside, threads_number, thread_items, _1), std::bind(MultithreadedPutRemove, threads_number, thread_items, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/concurrent/put_get_remove_single_op_params.cc000066400000000000000000000047741410000423300306070ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "unittest.hpp" #include using namespace pmem::kv; static void MultithreadedGetAndRemove(const size_t threads_number, pmem::kv::db &kv) { std::vector keys(threads_number, 0); std::iota(keys.begin(), keys.end(), 0); for (auto &k : keys) UT_ASSERT(kv.put(uint64_to_strv(k), uint64_to_strv(k)) == status::OK); /* test reading and removing data */ parallel_exec(threads_number, [&](size_t thread_id) { if (thread_id % 2 == 1) { auto s = kv.get(uint64_to_strv(keys[thread_id]), [&](string_view value) { UT_ASSERTeq(value.compare(uint64_to_strv( keys[thread_id])), 0); }); UT_ASSERTeq(s, status::OK); s = kv.get(uint64_to_strv(keys[thread_id - 1]), [&](string_view value) { UT_ASSERTeq(value.compare(uint64_to_strv( keys[thread_id - 1])), 0); }); UT_ASSERT(s == status::OK || s == status::NOT_FOUND); if (thread_id == threads_number - 1) return; s = kv.get(uint64_to_strv(keys[thread_id + 1]), [&](string_view value) { UT_ASSERTeq(value.compare(uint64_to_strv( keys[thread_id + 1])), 0); }); UT_ASSERT(s == status::OK || s == status::NOT_FOUND); } else { UT_ASSERTeq(kv.remove(uint64_to_strv(keys[thread_id])), status::OK); } }); } static void MultithreadedPutAndRemove(const size_t threads_number, pmem::kv::db &kv) { std::vector keys(threads_number, 0); std::iota(keys.begin(), keys.end(), 0); for (size_t i = 0; i < threads_number; i += 2) UT_ASSERTeq(kv.put(uint64_to_strv(keys[i]), uint64_to_strv(keys[i])), status::OK); /* test adding and removing data */ parallel_exec(threads_number, [&](size_t thread_id) { if (thread_id % 2 == 0) { UT_ASSERTeq(kv.remove(uint64_to_strv(keys[thread_id])), status::OK); } else { UT_ASSERTeq(kv.put(uint64_to_strv(keys[thread_id]), uint64_to_strv(keys[thread_id])), status::OK); } }); } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 4) UT_FATAL("usage: %s engine json_config threads", argv[0]); size_t threads_number = std::stoull(argv[3]); run_engine_tests(argv[1], argv[2], { std::bind(MultithreadedGetAndRemove, threads_number, _1), std::bind(MultithreadedPutAndRemove, threads_number, _1), }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/iterator.hpp000066400000000000000000000035171410000423300220240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ /* * Helper functions to test iterators. */ #include "../common/unittest.hpp" template using iterator = typename std::conditional::type; using pair = std::pair; using key_result = std::pair; static std::vector keys{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; void insert_keys(pmem::kv::db &kv) { std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(kv.put(p.first, p.second), pmem::kv::status::OK); }); } template void verify_key(iterator &it, pmem::kv::string_view expected) { auto result = it.key(); UT_ASSERT(result.is_ok()); UT_ASSERTeq(expected.compare(result.get_value()), 0); } template void verify_value(iterator &it, pmem::kv::string_view expected) { auto result = it.read_range(); UT_ASSERT(result.is_ok()); UT_ASSERTeq(expected.compare(result.get_value()), 0); } template typename std::enable_if>::type new_iterator(pmem::kv::db &kv) { auto res = kv.new_read_iterator(); UT_ASSERT(res.is_ok()); return std::move(res.get_value()); } template typename std::enable_if>::type new_iterator(pmem::kv::db &kv) { auto res = kv.new_write_iterator(); UT_ASSERT(res.is_ok()); return std::move(res.get_value()); } template static void verify_keys(iterator &it) { std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); }); } pmemkv-1.5.0/tests/engine_scenarios/memkind/000077500000000000000000000000001410000423300211005ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/memkind/error_handling.cc000066400000000000000000000050171410000423300244070ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include "pmem_allocator.h" #ifdef USE_LIBMEMKIND_NAMESPACE namespace memkind_ns = libmemkind::pmem; #else namespace memkind_ns = pmem; #endif static bool older_memkind; static void FailsToOpenInstanceWithInvalidPath(std::string engine, std::string non_existent_path) { pmem::kv::config cfg; auto s = cfg.put_path(non_existent_path); ASSERT_STATUS(s, pmem::kv::status::OK); s = cfg.put_size(MEMKIND_PMEM_MIN_SIZE); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; /* Non-existent path supplied */ s = kv.open(engine, std::move(cfg)); if (older_memkind) { ASSERT_STATUS(s, pmem::kv::status::UNKNOWN_ERROR); } else { ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } } static void FailsToCreateInstanceWithTooSmallSize(std::string engine, std::string path) { pmem::kv::config cfg; auto s = cfg.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = cfg.put_size(MEMKIND_PMEM_MIN_SIZE - 1); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; /* Too small size supplied */ s = kv.open(engine, std::move(cfg)); if (older_memkind) { ASSERT_STATUS(s, pmem::kv::status::UNKNOWN_ERROR); } else { ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } } static void NoSizeInConfig(std::string engine, std::string path) { pmem::kv::config cfg; auto s = cfg.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(cfg)); ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void NoPathInConfig(std::string engine) { pmem::kv::config cfg; auto s = cfg.put_size(MEMKIND_PMEM_MIN_SIZE); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(cfg)); ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine non_existent_path correct_path", argv[0]); /* Check if memkind has extended error handling XXX: Remove that when we won't support memkind < 1.12 */ try { auto alloc = memkind_ns::allocator(argv[2], 100000000); } catch (std::invalid_argument &e) { older_memkind = false; } catch (std::exception &e) { older_memkind = true; } FailsToOpenInstanceWithInvalidPath(argv[1], argv[2]); FailsToCreateInstanceWithTooSmallSize(argv[1], argv[3]); NoSizeInConfig(argv[1], argv[3]); NoPathInConfig(argv[1]); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/000077500000000000000000000000001410000423300216545ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/persistent/not_found_verify.cc000066400000000000000000000017271410000423300255510ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("tmpkey"), entry_from_string("tmpval1")), status::OK); } static void check(pmem::kv::db &kv) { std::string key = entry_from_string("tmpkey"); ASSERT_STATUS(kv.remove(key), status::OK); std::string value; ASSERT_STATUS(kv.get(key, &value), status::NOT_FOUND); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(kv); } else { check(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/overwrite_verify.cc000066400000000000000000000033501410000423300255760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(pmem::kv::db &kv) { std::string key = entry_from_string("key1"); std::string expected_value = entry_from_string("value1"); std::string value; ASSERT_STATUS(kv.put(key, expected_value), status::OK); ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == expected_value); expected_value = entry_from_string("VALUE1"); std::string new_value; ASSERT_STATUS(kv.put(key, expected_value), status::OK); // same size ASSERT_STATUS(kv.get(key, &new_value), status::OK); UT_ASSERT(new_value == expected_value); kv.close(); } static void check(pmem::kv::db &kv) { std::string key = entry_from_string("key1"); std::string expected_value = entry_from_string("new_val"); // longer size std::string new_value2; ASSERT_STATUS(kv.put(key, expected_value), status::OK); ASSERT_STATUS(kv.get(key, &new_value2), status::OK); UT_ASSERT(new_value2 == expected_value); expected_value = entry_from_string("?"); // shorter size std::string new_value3; ASSERT_STATUS(kv.put(key, expected_value), status::OK); ASSERT_STATUS(kv.get(key, &new_value3), status::OK); UT_ASSERT(new_value3 == expected_value); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(kv); } else { check(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/put_get_std_map_multiple_reopen.cc000066400000000000000000000014651410000423300306320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" const int N_ITERS = 50; static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 6) UT_FATAL("usage: %s engine json_config n_inserts key_length value_length", argv[0]); auto n_inserts = std::stoull(argv[3]); auto key_length = std::stoull(argv[4]); auto value_length = std::stoull(argv[5]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); auto proto = PutToMapTest(n_inserts, key_length, value_length, kv); for (int i = 0; i < N_ITERS; i++) { kv.close(); kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); VerifyKv(proto, kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/put_remove_verify.cc000066400000000000000000000030401410000423300257310ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("key1"), entry_from_string("value1")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key2"), entry_from_string("value2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key3"), entry_from_string("value3")), status::OK); ASSERT_STATUS(kv.remove(entry_from_string("key2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("key3"), entry_from_string("VALUE3")), status::OK); } static void check(pmem::kv::db &kv) { std::string value1; ASSERT_STATUS(kv.get(entry_from_string("key1"), &value1), status::OK); UT_ASSERT(value1 == entry_from_string("value1")); std::string value2; ASSERT_STATUS(kv.get(entry_from_string("key2"), &value2), status::NOT_FOUND); std::string value3; ASSERT_STATUS(kv.get(entry_from_string("key3"), &value3), status::OK); UT_ASSERT(value3 == entry_from_string("VALUE3")); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(kv); } else { check(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/put_verify.cc000066400000000000000000000035321410000423300243620ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("abc"), entry_from_string("A1")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("def"), entry_from_string("B2")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("hij"), entry_from_string("C3")), status::OK); } static void check(pmem::kv::db &kv) { ASSERT_STATUS(kv.put(entry_from_string("jkl"), entry_from_string("D4")), status::OK); ASSERT_STATUS(kv.put(entry_from_string("mno"), entry_from_string("E5")), status::OK); std::string value1; ASSERT_STATUS(kv.get(entry_from_string("abc"), &value1), status::OK); UT_ASSERT(value1 == entry_from_string("A1")); std::string value2; ASSERT_STATUS(kv.get(entry_from_string("def"), &value2), status::OK); UT_ASSERT(value2 == entry_from_string("B2")); std::string value3; ASSERT_STATUS(kv.get(entry_from_string("hij"), &value3), status::OK); UT_ASSERT(value3 == entry_from_string("C3")); std::string value4; ASSERT_STATUS(kv.get(entry_from_string("jkl"), &value4), status::OK); UT_ASSERT(value4 == entry_from_string("D4")); std::string value5; ASSERT_STATUS(kv.get(entry_from_string("mno"), &value5), status::OK); UT_ASSERT(value5 == entry_from_string("E5")); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config insert/check", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(kv); } else { check(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/put_verify_asc_params.cc000066400000000000000000000030021410000423300265430ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(const size_t iterations, pmem::kv::db &kv) { for (size_t i = 1; i <= iterations; i++) { auto key = entry_from_number(i); auto expected_value = entry_from_number(i, "", "!"); ASSERT_STATUS(kv.put(key, expected_value), status::OK); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == expected_value); } } static void check(const size_t iterations, pmem::kv::db &kv) { for (size_t i = 1; i <= iterations; i++) { auto key = entry_from_number(i); auto expected_value = entry_from_number(i, "", "!"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == expected_value); } std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == iterations); } static void test(int argc, char *argv[]) { if (argc < 5) UT_FATAL("usage: %s engine json_config insert/check iterations", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check iterations", argv[0]); auto iterations = std::stoull(argv[4]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(iterations, kv); } else { check(iterations, kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/persistent/put_verify_desc_params.cc000066400000000000000000000027751410000423300267330ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void insert(const size_t iterations, pmem::kv::db &kv) { for (size_t i = iterations; i >= 1; i--) { auto key = entry_from_number(i); auto expected_value = entry_from_number(i, "ABC"); ASSERT_STATUS(kv.put(key, expected_value), status::OK); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == expected_value); } } static void check(const size_t iterations, pmem::kv::db &kv) { for (size_t i = iterations; i >= 1; i--) { auto key = entry_from_number(i); auto expected_value = entry_from_number(i, "ABC"); std::string value; ASSERT_STATUS(kv.get(key, &value), status::OK); UT_ASSERT(value == expected_value); } std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == iterations); } static void test(int argc, char *argv[]) { if (argc < 5) UT_FATAL("usage: %s engine json_config insert/check iterations", argv[0]); std::string mode = argv[3]; if (mode != "insert" && mode != "check") UT_FATAL("usage: %s engine json_config insert/check iterations", argv[0]); auto iterations = std::stoull(argv[4]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "insert") { insert(iterations, kv); } else { check(iterations, kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/000077500000000000000000000000001410000423300211055ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/pmemobj/create_or_error_if_exists.cc000066400000000000000000000032761410000423300266550ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "unittest.hpp" /* * Tests for config flag create_or_error_if_exists with existing pool. */ static void FailsToOpenExisting(std::string path, std::string engine, std::size_t size) { /** * TEST: create_or_error_if_exists set to **true** should not work with existing * pool. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(size); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(true); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* It should err with "Failed creating pool - already exists" */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void OpenExisting(std::string path, std::string engine, std::size_t size) { /** * TEST: create_or_error_if_exists set to **false** should work with existing * pool. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(size); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(false); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); ASSERT_STATUS(s, pmem::kv::status::OK); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine path size", argv[0]); auto engine = argv[1]; auto path = argv[2]; size_t size = std::stoul(argv[3]); FailsToOpenExisting(path, engine, size); OpenExisting(path, engine, size); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_create.cc000066400000000000000000000155461410000423300257470ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include /* * Error handling for pmemobj-based engines, for opening & creating pool. */ static void FailsToCreateInstanceWithNonExistentPath(std::string non_existent_path, std::string engine, bool create_flag) { pmem::kv::config config; auto s = config.put_path(non_existent_path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Non-existent path supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithHugeSize(std::string path, std::string engine, bool create_flag) { pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(std::numeric_limits::max()); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Too big pool size supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithTinySize(std::string path, std::string engine, bool create_flag) { pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(PMEMOBJ_MIN_POOL - 1); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Too small pool size supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithNoSize(std::string path, std::string engine, bool create_flag) { pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* No size supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithPathAndOid(std::string path, std::string engine, bool create_flag) { PMEMoid oid; pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_oid(&oid); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Both path and oid supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToOpenInstanceWithBothFlagsFalse(std::string path, std::string engine) { /** * TEST: no flags set, it will try to open a non-existent pool. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(false); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(false); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Open should fail since there's no pool */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToOpenInstanceWithBothFlagsTrue(std::string path, std::string engine) { /** * TEST: both flags set, it's disallowed. */ pmem::kv::config config; auto s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_or_error_if_exists(true); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(true); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Flags are mutualy exclusive, it should fail if both set */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithNoPathOrOid(std::string path, std::string engine, bool create_flag) { pmem::kv::config config; auto s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* No path and no oid supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } static void FailsToCreateInstanceWithCornerCasePaths(std::string engine, bool create_flag) { std::vector paths = {"/", "", "//", ",./;'[]-=<>?:\"{}|_+!@#$%^&*()`~"}; for (auto &path : paths) { pmem::kv::config config; auto s = config.put_create_or_error_if_exists(create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_create_if_missing(!create_flag); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_size(5 * PMEMOBJ_MIN_POOL); ASSERT_STATUS(s, pmem::kv::status::OK); s = config.put_path(path); ASSERT_STATUS(s, pmem::kv::status::OK); pmem::kv::db kv; s = kv.open(engine, std::move(config)); /* Invalid path supplied */ ASSERT_STATUS(s, pmem::kv::status::INVALID_ARGUMENT); } } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine path non_existent_path", argv[0]); auto engine = argv[1]; auto path = argv[2]; auto non_existent_path = argv[3]; for (auto flag : {true, false}) { FailsToCreateInstanceWithNonExistentPath(non_existent_path, engine, flag); FailsToCreateInstanceWithHugeSize(path, engine, flag); FailsToCreateInstanceWithTinySize(path, engine, flag); FailsToCreateInstanceWithNoSize(path, engine, flag); FailsToCreateInstanceWithPathAndOid(path, engine, flag); FailsToCreateInstanceWithNoPathOrOid(path, engine, flag); FailsToCreateInstanceWithCornerCasePaths(engine, flag); } FailsToOpenInstanceWithBothFlagsFalse(path, engine); FailsToOpenInstanceWithBothFlagsTrue(path, engine); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_defrag.cc000066400000000000000000000012321410000423300257170ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "unittest.hpp" static void DefragInvalidArgument(pmem::kv::db &kv) { ASSERT_STATUS(kv.defrag(50, 100), pmem::kv::status::INVALID_ARGUMENT); ASSERT_STATUS(kv.defrag(0, 101), pmem::kv::status::INVALID_ARGUMENT); ASSERT_STATUS(kv.defrag(101, 0), pmem::kv::status::INVALID_ARGUMENT); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); DefragInvalidArgument(kv); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_tx.hpp000066400000000000000000000027651410000423300253400ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include using namespace pmem::kv; // XXX should this be per engine or implemented in some generic way, e.g. // UT_ASSERT(kv.some_func() == NOT_SUPPORTED // || kv.some_func() == TRANSACTION_SCOPE_ERROR); ? static void TransactionTest(pmem::obj::pool_base &pmemobj_pool, pmem::kv::db &kv) { std::string value; ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::NOT_FOUND); pmem::obj::transaction::run(pmemobj_pool, [&] { ASSERT_STATUS( kv.put(entry_from_string("key1"), entry_from_string("value1")), status::TRANSACTION_SCOPE_ERROR); }); pmem::obj::transaction::run(pmemobj_pool, [&] { ASSERT_STATUS(kv.get(entry_from_string("key1"), &value), status::TRANSACTION_SCOPE_ERROR); }); pmem::obj::transaction::run(pmemobj_pool, [&] { ASSERT_STATUS(kv.remove(entry_from_string("key1")), status::TRANSACTION_SCOPE_ERROR); }); pmem::obj::transaction::run(pmemobj_pool, [&] { ASSERT_STATUS(kv.exists(entry_from_string("key1")), status::TRANSACTION_SCOPE_ERROR); }); pmem::obj::transaction::run(pmemobj_pool, [&] { ASSERT_STATUS(kv.get_all([&](pmem::kv::string_view key, pmem::kv::string_view value) { return 0; }), status::TRANSACTION_SCOPE_ERROR); }); pmem::obj::transaction::run(pmemobj_pool, [&] { size_t cnt; ASSERT_STATUS(kv.count_all(cnt), status::TRANSACTION_SCOPE_ERROR); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_tx_oid.cc000066400000000000000000000020041410000423300257530ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "error_handling_tx.hpp" #include #include struct Root { PMEMoid oid; }; static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 3) UT_FATAL("usage: %s engine path", argv[0]); auto pmemobj_pool_path = std::string(argv[2]); pmem::obj::pool pmemobj_pool; auto layout = std::string("pmemkv"); auto engine = std::string(argv[1]); if (engine != "cmap") layout = "pmemkv_" + engine; try { pmemobj_pool = pmem::obj::pool::open(pmemobj_pool_path, layout); } catch (std::exception &e) { UT_FATALexc(e); } pmem::kv::config cfg; auto s = cfg.put_oid(&(pmemobj_pool.root()->oid)); ASSERT_STATUS(s, status::OK); { auto kv = INITIALIZE_KV(engine, std::move(cfg)); TransactionTest(pmemobj_pool, kv); } pmemobj_pool.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_tx_oom.cc000066400000000000000000000042631410000423300260030ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "mock_tx_alloc.h" #include "unittest.hpp" const std::string LONGSTR = "123456789A123456789A123456789A123456789A123456789A123456789A123456789A"; const int init_iterations = 50000; void validate(pmem::kv::db &kv) { for (size_t i = 0; i < init_iterations; i++) { std::string istr = std::to_string(i); auto s = kv.get(istr, [&](pmem::kv::string_view val) { UT_ASSERT(val.compare(istr + "!") == 0); }); ASSERT_STATUS(s, pmem::kv::status::OK); } } void populate(pmem::kv::db &kv) { for (size_t i = 0; i < init_iterations; i++) { std::string istr = std::to_string(i); ASSERT_STATUS(kv.put(istr, (istr + "!")), pmem::kv::status::OK); } } void LongStringTest(pmem::kv::db &kv) { populate(kv); ASSERT_STATUS(kv.remove("100"), pmem::kv::status::OK); tx_alloc_should_fail = true; ASSERT_STATUS(kv.put("100", LONGSTR), pmem::kv::status::OUT_OF_MEMORY); tx_alloc_should_fail = false; ASSERT_STATUS(kv.put("100", "100!"), pmem::kv::status::OK); validate(kv); } void ShortKeyTest(pmem::kv::db &kv) { populate(kv); tx_alloc_should_fail = true; for (int i = 0; i <= 99999; i++) { ASSERT_STATUS(kv.put("123456", LONGSTR), pmem::kv::status::OUT_OF_MEMORY); } tx_alloc_should_fail = false; ASSERT_STATUS(kv.remove("4567"), pmem::kv::status::OK); ASSERT_STATUS(kv.put("4567", "4567!"), pmem::kv::status::OK); validate(kv); } void LongKeyTest(pmem::kv::db &kv) { populate(kv); tx_alloc_should_fail = true; for (int i = 0; i <= 99999; i++) { ASSERT_STATUS(kv.put(LONGSTR, "1"), pmem::kv::status::OUT_OF_MEMORY); ASSERT_STATUS(kv.put(LONGSTR, LONGSTR), pmem::kv::status::OUT_OF_MEMORY); } tx_alloc_should_fail = false; ASSERT_STATUS(kv.remove("34567"), pmem::kv::status::OK); ASSERT_STATUS(kv.put("34567", "34567!"), pmem::kv::status::OK); validate(kv); } static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], { LongStringTest, ShortKeyTest, LongKeyTest, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/error_handling_tx_path.cc000066400000000000000000000014741410000423300261460ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "error_handling_tx.hpp" static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 4) UT_FATAL("usage: %s engine json_config obj_path", argv[0]); auto pmemobj_pool_path = std::string(argv[3]); pmem::obj::pool_base pmemobj_pool; auto layout = std::string("pmemkv"); auto engine = std::string(argv[1]); if (engine != "cmap") layout = "pmemkv_" + engine; try { pmemobj_pool = pmem::obj::pool_base::open(pmemobj_pool_path, layout); } catch (std::exception &e) { UT_FATALexc(e); } auto kv = INITIALIZE_KV(engine, CONFIG_FROM_JSON(argv[2])); TransactionTest(pmemobj_pool, kv); pmemobj_pool.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/mock_tx_alloc.cc000066400000000000000000000016231410000423300242340ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2019, Intel Corporation */ #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.5.0/tests/engine_scenarios/pmemobj/mock_tx_alloc.h000066400000000000000000000002241410000423300240720ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2020, Intel Corporation */ #pragma once extern thread_local bool tx_alloc_should_fail; pmemkv-1.5.0/tests/engine_scenarios/pmemobj/put_get_std_map_defrag.cc000066400000000000000000000013621410000423300261040ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 6) UT_FATAL("usage: %s engine json_config n_inserts key_length value_length", argv[0]); auto n_inserts = std::stoull(argv[3]); auto key_length = std::stoull(argv[4]); auto value_length = std::stoull(argv[5]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); auto proto = PutToMapTest(n_inserts, key_length, value_length, kv); auto s = kv.defrag(0, 100); ASSERT_STATUS(s, pmem::kv::status::OK); VerifyKv(proto, kv); kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmemobj/put_get_std_map_oid.cc000066400000000000000000000023421410000423300254260ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" #include #include struct Root { PMEMoid oid; }; static void test(int argc, char *argv[]) { using namespace std::placeholders; if (argc < 6) UT_FATAL("usage: %s engine path n_inserts key_length value_length", argv[0]); auto n_inserts = std::stoull(argv[3]); auto key_length = std::stoull(argv[4]); auto value_length = std::stoull(argv[5]); auto pmemobj_pool_path = std::string(argv[2]); pmem::obj::pool pmemobj_pool; auto layout = std::string("pmemkv"); auto engine = std::string(argv[1]); if (engine != "cmap") layout = "pmemkv_" + engine; try { pmemobj_pool = pmem::obj::pool::open(pmemobj_pool_path, layout); } catch (std::exception &e) { UT_FATALexc(e); } pmem::kv::config cfg; auto s = cfg.put_oid(&pmemobj_pool.root()->oid); ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(cfg)); auto proto = PutToMapTest(n_inserts, key_length, value_length, kv); VerifyKv(proto, kv); kv.close(); pmemobj_pool.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmreorder/000077500000000000000000000000001410000423300214535ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/pmreorder/erase.cc000066400000000000000000000044021410000423300230610ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * erase.cc -- erase pmreorder test */ #include "unittest.hpp" static constexpr size_t len_elements = 10; static void check_exist(pmem::kv::db &kv, const std::string &element, pmem::kv::status exists) { std::string value; UT_ASSERT(kv.get(element, &value) == exists); if (exists == pmem::kv::status::OK) { UT_ASSERT(element == value); } } static void test_init(pmem::kv::db &kv) { for (size_t i = 0; i < len_elements; i++) { std::string element = entry_from_number(i); ASSERT_STATUS(kv.put(element, element), pmem::kv::status::OK); check_exist(kv, element, pmem::kv::status::OK); } } static void test_erase(pmem::kv::db &kv) { std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); UT_ASSERT(size == len_elements); std::string element = entry_from_number(1); /* remove this element */ check_exist(kv, element, pmem::kv::status::OK); ASSERT_STATUS(kv.remove(element), pmem::kv::status::OK); check_exist(kv, element, pmem::kv::status::NOT_FOUND); } static void check_consistency(pmem::kv::db &kv) { std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); std::size_t count = 0; for (size_t i = 0; i < len_elements; i++) { std::string element = entry_from_number(i); if (kv.exists(element) == pmem::kv::status::OK) { ++count; check_exist(kv, element, pmem::kv::status::OK); } else { check_exist(kv, element, pmem::kv::status::NOT_FOUND); } } UT_ASSERTeq(count, size); } static void test(int argc, char *argv[]) { std::cout << "ARGC: " << argc << std::endl; for (int i = 0; i < argc; ++i) { std::cout << "ARGV " << i << " : " << argv[i] << std::endl; } if (argc < 4) UT_FATAL("usage: %s engine json_config ", argv[0]); std::string mode = argv[3]; if (mode != "create" && mode != "open" && mode != "erase") UT_FATAL("usage: %s engine json_config ", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "create") { test_init(kv); } else if (mode == "open") { check_consistency(kv); } else if (mode == "erase") { test_erase(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmreorder/insert.cc000066400000000000000000000043111410000423300232650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ /* * insert.cc -- insert pmreorder test */ #include "unittest.hpp" static constexpr size_t len_elements = 10; static void check_exist(pmem::kv::db &kv, const std::string &element, pmem::kv::status exists) { std::string value; UT_ASSERT(kv.get(element, &value) == exists); if (exists == pmem::kv::status::OK) { UT_ASSERT(element == value); } } static void test_init(pmem::kv::db &kv) { for (size_t i = 0; i < len_elements; i++) { std::string element = entry_from_number(i); ASSERT_STATUS(kv.put(element, element), pmem::kv::status::OK); check_exist(kv, element, pmem::kv::status::OK); } } static void test_insert(pmem::kv::db &kv) { std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); UT_ASSERT(size == len_elements); std::string element = entry_from_number(len_elements); ASSERT_STATUS(kv.put(element, element), pmem::kv::status::OK); check_exist(kv, element, pmem::kv::status::OK); } static void check_consistency(pmem::kv::db &kv) { std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); std::size_t count = 0; for (size_t i = 0; i <= len_elements; i++) { std::string element = entry_from_number(i); if (kv.exists(element) == pmem::kv::status::OK) { ++count; check_exist(kv, element, pmem::kv::status::OK); } else { check_exist(kv, element, pmem::kv::status::NOT_FOUND); } } UT_ASSERTeq(count, size); } static void test(int argc, char *argv[]) { std::cout << "ARGC: " << argc << std::endl; for (int i = 0; i < argc; ++i) { std::cout << "ARGV " << i << " : " << argv[i] << std::endl; } if (argc < 4) UT_FATAL("usage: %s engine json_config ", argv[0]); std::string mode = argv[3]; if (mode != "create" && mode != "open" && mode != "insert") UT_FATAL("usage: %s engine json_config ", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "create") { test_init(kv); } else if (mode == "open") { check_consistency(kv); } else if (mode == "insert") { test_insert(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmreorder/iterator.cc000066400000000000000000000054611410000423300236210ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ /* * iterator.cc -- iterator pmreorder test (iterator has to support seek_to_first()) */ #include "../iterator.hpp" static constexpr size_t elements_length = 20; static void check_exist(pmem::kv::db &kv, const std::string &key, const std::string &value) { auto it = new_iterator(kv); UT_ASSERTeq(it.seek(key), pmem::kv::status::OK); auto res = it.read_range(); UT_ASSERT(res.is_ok()); UT_ASSERTeq(value.compare(res.get_value().begin()), 0); } static void test_init(pmem::kv::db &kv) { for (size_t i = 0; i < elements_length; i++) { auto key = std::to_string(i); auto val = std::string(elements_length, static_cast(i + 10)); ASSERT_STATUS(kv.put(key, val), pmem::kv::status::OK); check_exist(kv, key, val); } } /* write first element's value to 'x' */ static void test_write(pmem::kv::db &kv) { auto it = new_iterator(kv); std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); UT_ASSERT(size == elements_length); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'x'; it.commit(); check_exist(kv, "0", std::string(elements_length, 'x')); } static void check_consistency(pmem::kv::db &kv) { auto it = new_iterator(kv); std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); std::size_t count = 0; for (size_t i = 1; i < elements_length; i++) { std::string key = std::to_string(i); std::string val = std::string(elements_length, static_cast(i + 10)); ASSERT_STATUS(it.seek(key), pmem::kv::status::OK); ++count; check_exist(kv, key, val); } /* check first element's value */ ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); auto res = it.read_range(); UT_ASSERT(res.is_ok()); std::string value = res.get_value().data(); UT_ASSERT(value.compare(std::string(elements_length, (char)10)) == 0 || value.compare(std::string(elements_length, 'x')) == 0); ++count; UT_ASSERTeq(count, size); } static void test(int argc, char *argv[]) { std::cout << "ARGC: " << argc << std::endl; for (int i = 0; i < argc; ++i) { std::cout << "ARGV " << i << " : " << argv[i] << std::endl; } if (argc < 4) UT_FATAL("usage: %s engine json_config ", argv[0]); std::string mode = argv[3]; if (mode != "create" && mode != "open" && mode != "write") UT_FATAL("usage: %s engine json_config ", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "create") { test_init(kv); } else if (mode == "open") { check_consistency(kv); } else if (mode == "write") { test_write(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/pmreorder/recover.cc000066400000000000000000000030321410000423300234250ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ /* * recover.cc -- recover pmreorder test */ #include "unittest.hpp" static void check_consistency(std::string engine, std::string config) { auto kv = INITIALIZE_KV(engine, CONFIG_FROM_JSON(config)); size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, 0); for (size_t i = 0; i < 100; ++i) ASSERT_STATUS(kv.put(entry_from_number(i, "", "key"), entry_from_number(i, "", "val")), pmem::kv::status::OK); ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, 100); for (size_t i = 0; i < 100; ++i) { std::string val; ASSERT_STATUS(kv.get(entry_from_number(i, "", "key"), &val), pmem::kv::status::OK); UT_ASSERT(val == entry_from_number(i, "", "val")); ASSERT_STATUS(kv.remove(entry_from_number(i, "", "key")), pmem::kv::status::OK); } ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, 0); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 4) UT_FATAL("usage: %s engine json_config ", argv[0]); std::string mode = argv[3]; if (mode != "open" && mode != "create") UT_FATAL("usage: %s engine json_config ", argv[0]); if (mode == "open") { check_consistency(argv[1], argv[2]); } else if (mode == "create") { INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); } } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/put_get_std_map.hpp000066400000000000000000000027461410000423300233540ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "unittest.hpp" #include #include using namespace pmem::kv; template std::map PutToMapTest(size_t n_inserts, size_t key_length, size_t value_length, Inserter &kv) { /** * Test: Put data into db and get it back */ std::map proto_dictionary; for (size_t i = 0; i < n_inserts; i++) { std::string key = std::to_string(i); if (key_length > key.length()) key += std::string(key_length - key.length(), '0'); std::string val = std::to_string(i); if (value_length > val.length()) val += std::string(value_length - val.length(), '0'); proto_dictionary[key] = val; } /* Put data into db */ for (const auto &record : proto_dictionary) { const auto &key = record.first; const auto &val = record.second; auto s = kv.put(key, val); ASSERT_STATUS(s, status::OK); } return proto_dictionary; } void VerifyKv(const std::map &prototype, pmem::kv::db &kv) { size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(prototype.size(), cnt); /* Retrieve data from db and compare with prototype */ for (const auto &record : prototype) { const auto &key = record.first; const auto &val = record.second; auto s = kv.get(key, [&](string_view value) { UT_ASSERT(value.compare(val) == 0); }); ASSERT_STATUS(s, status::OK); } } pmemkv-1.5.0/tests/engine_scenarios/sorted/000077500000000000000000000000001410000423300207545ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/sorted/get_above_gen_params.cc000066400000000000000000000302101410000423300254060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" /** * Generated tests for get_above and count_above methods for sorted engines. * get_above method returns all elements in db with keys greater than the given key * (count returns the number of such records). */ static void GetAboveTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above(kv, EMPTY_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_above(kv, EMPTY_KEY, 6, kv_sort(expected)); expected = kv_list{{"BB", "5"}, {"BC", "6"}}; verify_get_above(kv, "B", 2, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_above(kv, "B", 3, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_above(kv, EMPTY_KEY, 7, kv_sort(expected)); verify_get_above(kv, "ZZZ", 0, kv_list()); expected = kv_list{{"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_above(kv, "BA", 3, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); /* testing C-like API */ expected = kv_list{{"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"记!", "RR"}}; verify_get_above_c(kv, "B", 4, kv_sort(expected)); verify_get_above_c(kv, "记!", 0, kv_list()); CLEAR_KV(kv); verify_get_above_c(kv, MIN_KEY, 0, kv_list()); kv.close(); } static void GetAboveReverseTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above(kv, EMPTY_KEY, 0, {}); verify_get_above_c(kv, MAX_KEY, 0, {}); /* insert bunch of keys */ add_basic_keys(kv); verify_get_above(kv, EMPTY_KEY, 0, {}); auto expected = kv_list{{"AC", "3"}, {"AB", "2"}, {"A", "1"}}; verify_get_above(kv, "B", 3, expected); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"AC", "3"}, {"AB", "2"}, {"A", "1"}}; verify_get_above(kv, "B", 3, expected); expected = kv_list{{"BD", "7"}, {"BC", "6"}, {"BB", "5"}, {"B", "4"}, {"AC", "3"}, {"AB", "2"}, {"A", "1"}}; verify_get_above(kv, MAX_KEY, 7, expected); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); /* testing C-like API */ expected = kv_list{{"AC", "3"}, {"AB", "2"}, {"A", "1"}}; verify_get_above_c(kv, "B", 3, expected); expected = kv_list{{"BD", "7"}, {"BC", "6"}, {"BB", "5"}, {"B", "4"}, {"AC", "3"}, {"AB", "2"}, {"A", "1"}}; verify_get_above_c(kv, "记!", 7, expected); CLEAR_KV(kv); verify_get_above_c(kv, MIN_KEY, 0, {}); verify_get_above_c(kv, MAX_KEY, 0, {}); kv.close(); } static void GetAboveTest2(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some keys are removed. * This test is using C-like API. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above_c(kv, MIN_KEY, 0, kv_list()); /* insert bunch of keys */ add_ext_keys(kv); auto expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_above_c(kv, MIN_KEY, 7, kv_sort(expected)); expected = kv_list{{"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_above_c(kv, "ccc", 4, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_above_c(kv, "a", 7, kv_sort(expected)); expected = kv_list{{"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_above_c(kv, "ddd", 4, kv_sort(expected)); /* remove one key */ ASSERT_STATUS(kv.remove("sss"), status::OK); expected = kv_list{{"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_above_c(kv, "ddd", 3, kv_sort(expected)); verify_get_above_c(kv, "z", 0, kv_list()); CLEAR_KV(kv); verify_get_above_c(kv, MAX_KEY, 0, kv_list()); kv.close(); } static void GetAboveRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above(kv, "randtest", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_above(kv, MIN_KEY, i + 1, kv_sort(expected)); /* verifies elements above the first one */ auto exp_sorted = kv_sort(expected); verify_get_above(kv, exp_sorted[0].first, i, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2; verify_get_above( kv, exp_sorted[half - 1].first, exp_sorted.size() - half, kv_list(exp_sorted.begin() + half, exp_sorted.end())); } if (exp_sorted.size() > 5) { /* verifies last few elements */ verify_get_above(kv, exp_sorted[exp_sorted.size() - 5].first, 4, kv_list(exp_sorted.end() - 4, exp_sorted.end())); } } CLEAR_KV(kv); kv.close(); } static void GetAboveIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated keys with incremental keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_above returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above(kv, "a_inc", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_above(kv, MIN_KEY, i + 1, kv_sort(expected)); /* verifies elements above the first one */ auto exp_sorted = kv_sort(expected); verify_get_above(kv, exp_sorted[0].first, i, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2; verify_get_above( kv, exp_sorted[half - 1].first, exp_sorted.size() - half, kv_list(exp_sorted.begin() + half, exp_sorted.end())); } } /* start over with two initial keys */ CLEAR_KV(kv); ASSERT_STATUS(kv.put(MAX_KEY, "init0"), status::OK); ASSERT_STATUS(kv.put(MAX_KEY + MAX_KEY, "init1"), status::OK); expected = kv_list{{MAX_KEY, "init0"}, {MAX_KEY + MAX_KEY, "init1"}}; verify_get_above(kv, MIN_KEY, 2, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); keys_cnt = charset_size * max_key_len; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_above(kv, MIN_KEY, i + 3, kv_sort(expected)); /* verifies elements from 2nd to last */ auto exp_sorted = kv_sort(expected); verify_get_above(kv, exp_sorted[0].first, i + 2, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); } CLEAR_KV(kv); kv.close(); } static void GetAboveIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated keys with incremental keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_above returns properly all * data. After initial part of the test, some keys are deleted and some new keys * are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_above(kv, "&Rev&", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_above(kv, MIN_KEY, keys_cnt - i + 1, kv_sort(expected)); /* verifies elements above the first one */ auto exp_sorted = kv_sort(expected); verify_get_above(kv, exp_sorted[0].first, keys_cnt - i, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies above 11th element */ auto exp_sorted = kv_sort(expected); verify_get_above_c(kv, exp_sorted[10].first, keys_cnt - 11, kv_list(exp_sorted.begin() + 11, exp_sorted.end())); /* verifies all elements */ verify_get_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); /* remove 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); /* remove 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 6) UT_FATAL("usage: %s engine json_config comparator items max_key_len", argv[0]); auto engine = std::string(argv[1]); auto comparator = std::string(argv[3]); size_t items = std::stoull(argv[4]); size_t max_key_len = std::stoull(argv[5]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); if (comparator == "default") { GetAboveTest(engine, CONFIG_FROM_JSON(argv[2])); GetAboveTest2(engine, CONFIG_FROM_JSON(argv[2])); GetAboveRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetAboveIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetAboveIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } else if (comparator == "reverse") { auto make_config = [&] { auto cfg = CONFIG_FROM_JSON(argv[2]); ASSERT_STATUS(cfg.put_comparator(reverse_comparator{}), status::OK); return cfg; }; GetAboveReverseTest(engine, make_config()); } else UT_FATAL("Unknown comparator"); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/get_all_gen_params.cc000066400000000000000000000176341410000423300251010ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" /** * Basic + generated tests for get_all and count_all methods for sorted engines. * get_all method returns all elements in db (count returns the number of all records). */ static void GetAllTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. New keys added, some keys removed. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_all(kv, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_all(kv, 6, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_all(kv, 7, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("AA", "8"), status::OK); expected = kv_list{{"A", "1"}, {"AA", "8"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_all(kv, 8, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); expected = kv_list{{"A", "1"}, {"AA", "8"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"记!", "RR"}}; verify_get_all(kv, 9, kv_sort(expected)); /* insert bunch of new keys */ add_ext_keys(kv); /* testing C-like API */ expected = kv_list{{"A", "1"}, {"AA", "8"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}, {"记!", "RR"}}; verify_get_all_c(kv, 16, kv_sort(expected)); /* remove two keys */ ASSERT_STATUS(kv.remove("A"), status::OK); ASSERT_STATUS(kv.remove("BC"), status::OK); /* testing C-like API */ expected = kv_list{{"AA", "8"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BD", "7"}, {"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}, {"记!", "RR"}}; verify_get_all_c(kv, 14, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void GetAllRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_all(kv, 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); verify_get_all(kv, i + 1, kv_sort(expected)); } CLEAR_KV(kv); kv.close(); } static void GetAllIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_all returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_all(kv, 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); const size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); verify_get_all(kv, i + 1, kv_sort(expected)); } /* start over with 3 initial keys */ CLEAR_KV(kv); const std::string mid_key = std::string(2, char(127)); ASSERT_STATUS(kv.put(MIN_KEY, "init0"), status::OK); ASSERT_STATUS(kv.put(mid_key, "init1"), status::OK); ASSERT_STATUS(kv.put(MAX_KEY, "init2"), status::OK); expected = kv_list{{MIN_KEY, "init0"}, {mid_key, "init1"}, {MAX_KEY, "init2"}}; /* testing C-like API */ verify_get_all_c(kv, 3, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); verify_get_all(kv, i + 4, kv_sort(expected)); } CLEAR_KV(kv); verify_get_all(kv, 0, kv_list()); kv.close(); } static void GetAllIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_all returns properly all * data. After initial part of the test, some keys are deleted and some new keys * are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_all(kv, 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); verify_get_all(kv, keys_cnt - i + 1, kv_sort(expected)); } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove and check at 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; verify_get_all_c(kv, keys_cnt, kv_sort(expected)); /* remove and check at 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; verify_get_all_c(kv, keys_cnt, kv_sort(expected)); /* remove and check at 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; verify_get_all_c(kv, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_all_c(kv, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_all_c(kv, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config items max_key_len", argv[0]); auto engine = std::string(argv[1]); size_t items = std::stoull(argv[3]); size_t max_key_len = std::stoull(argv[4]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); GetAllTest(engine, CONFIG_FROM_JSON(argv[2])); GetAllRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetAllIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetAllIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/get_below_gen_params.cc000066400000000000000000000260021410000423300254260ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" #include /** * Basic + generated tests for get_below and count_below methods for sorted engines. * get_below method returns all elements in db with keys lesser than the given key * (count returns the number of such records). */ static void GetBelowTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_below(kv, EMPTY_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_below(kv, MAX_KEY, 6, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}}; verify_get_below(kv, "B", 3, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}}; verify_get_below(kv, "B", 3, kv_sort(expected)); verify_get_below(kv, EMPTY_KEY, 0, kv_list()); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_below(kv, "ZZZ", 7, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_below(kv, "BD", 6, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_below(kv, "ZZZ", 7, kv_sort(expected)); expected.emplace_back("记!", "RR"); verify_get_below(kv, MAX_KEY, 8, kv_sort(expected)); /* testing C-like API */ expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_below_c(kv, "BE", 7, kv_sort(expected)); verify_get_below_c(kv, "记!", 7, kv_sort(expected)); CLEAR_KV(kv); verify_get_below_c(kv, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBelowTest2(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some keys are removed. * This test is using C-like API. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_below_c(kv, MAX_KEY, 0, kv_list()); /* insert bunch of keys */ add_ext_keys(kv); auto expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_below_c(kv, MAX_KEY, 7, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}}; verify_get_below_c(kv, "ccc", 2, kv_sort(expected)); verify_get_below_c(kv, "a", 0, kv_list()); verify_get_below_c(kv, EMPTY_KEY, 0, kv_list()); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}}; verify_get_below_c(kv, "ddd", 3, kv_sort(expected)); /* remove one key */ ASSERT_STATUS(kv.remove("sss"), status::OK); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_below_c(kv, MAX_KEY, 6, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_below_c(kv, "z", 6, kv_sort(expected)); CLEAR_KV(kv); verify_get_below_c(kv, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBelowRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_below(kv, "randtest", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_below(kv, MAX_KEY, i + 1, kv_sort(expected)); /* verifies elements below the last one */ auto exp_sorted = kv_sort(expected); verify_get_below(kv, exp_sorted[i].first, i, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2 + 1; verify_get_below(kv, exp_sorted[half - 1].first, half - 1, kv_list(exp_sorted.begin(), exp_sorted.begin() + half - 1)); } if (exp_sorted.size() > 5) { /* verifies first few elements */ verify_get_below( kv, exp_sorted[4].first, 4, kv_list(exp_sorted.begin(), exp_sorted.begin() + 4)); } } CLEAR_KV(kv); kv.close(); } static void GetBelowIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_below returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_below(kv, "a_inc", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_below(kv, MAX_KEY, i + 1, kv_sort(expected)); /* verifies elements below the last one */ auto exp_sorted = kv_sort(expected); verify_get_below(kv, exp_sorted[i].first, i, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2 + 1; verify_get_below(kv, exp_sorted[half - 1].first, half - 1, kv_list(exp_sorted.begin(), exp_sorted.begin() + half - 1)); } } /* start over with two initial keys */ CLEAR_KV(kv); ASSERT_STATUS(kv.put(MIN_KEY, "init0"), status::OK); ASSERT_STATUS(kv.put(MIN_KEY + MIN_KEY, "init1"), status::OK); expected = kv_list{{MIN_KEY, "init0"}, {MIN_KEY + MIN_KEY, "init1"}}; verify_get_below(kv, MAX_KEY, 2, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); keys_cnt = charset_size * max_key_len; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); if (i % 5 == 0) { /* verifies all elements */ verify_get_below(kv, MAX_KEY, i + 3, kv_sort(expected)); /* verifies elements below the last one */ auto exp_sorted = kv_sort(expected); verify_get_below( kv, exp_sorted[i + 2].first, i + 2, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); } } CLEAR_KV(kv); kv.close(); } static void GetBelowIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_below returns properly all * data. After initial part of the test, some keys are deleted and some new keys * are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_below(kv, "&Rev&", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); size_t curr_iter = keys_cnt - i; /* verifies all elements */ verify_get_below(kv, MAX_KEY, curr_iter + 1, kv_sort(expected)); /* verifies elements below the last one */ auto exp_sorted = kv_sort(expected); verify_get_below(kv, exp_sorted[curr_iter].first, curr_iter, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies below 19-th element */ auto exp_sorted = kv_sort(expected); verify_get_below_c(kv, exp_sorted[18].first, 18, kv_list(exp_sorted.begin(), exp_sorted.begin() + 18)); /* verifies all elements */ verify_get_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config items max_key_len", argv[0]); auto engine = std::string(argv[1]); size_t items = std::stoull(argv[3]); size_t max_key_len = std::stoull(argv[4]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); GetBelowTest(engine, CONFIG_FROM_JSON(argv[2])); GetBelowTest2(engine, CONFIG_FROM_JSON(argv[2])); GetBelowRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetBelowIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetBelowIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/get_between_gen_params.cc000066400000000000000000000351241410000423300257540ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" /** * Basic + generated tests for get_between and count_between methods for sorted engines. * get_between method returns all elements in db with keys greater than key1 * and lesser than key2 (count returns the number of such records). */ static void GetBetweenTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_between(kv, MIN_KEY, MAX_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_between(kv, EMPTY_KEY, MAX_KEY, 6, kv_sort(expected)); expected = kv_list{{"AB", "2"}, {"AC", "3"}}; verify_get_between(kv, "A", "B", 2, kv_sort(expected)); expected = kv_list{{"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_between(kv, "A", "C", 5, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_between(kv, "A", "C", 6, kv_sort(expected)); expected = kv_list{{"BB", "5"}, {"BC", "6"}}; verify_get_between(kv, "B", "BD", 2, kv_sort(expected)); expected = kv_list{{"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_between(kv, "B", "BE", 3, kv_sort(expected)); expected = kv_list{{"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_between(kv, "AZ", "BE", 4, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_between(kv, EMPTY_KEY, "ZZZ", 7, kv_sort(expected)); verify_get_between(kv, MIN_KEY, MAX_KEY, 7, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); /* testing C-like API */ expected = kv_list{{"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"记!", "RR"}}; verify_get_between_c(kv, "B", MAX_KEY, 4, kv_sort(expected)); expected = kv_list{{"BC", "6"}, {"BD", "7"}}; verify_get_between_c(kv, "BB", "记!", 2, kv_sort(expected)); expected = kv_list{{"BD", "7"}, {"记!", "RR"}}; verify_get_between_c(kv, "BC", MAX_KEY, 2, kv_sort(expected)); expected = kv_list{{"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"记!", "RR"}}; verify_get_between_c(kv, "AAA", "\xFF", 7, kv_sort(expected)); /* empty/wrong range */ verify_get_between_c(kv, EMPTY_KEY, EMPTY_KEY, 0, kv_list()); verify_get_between_c(kv, "BB", "BB", 0, kv_list()); verify_get_between_c(kv, "BX", "BX", 0, kv_list()); verify_get_between_c(kv, "BA", "A", 0, kv_list()); verify_get_between_c(kv, "记!", "BB", 0, kv_list()); verify_get_between_c(kv, "记!", MIN_KEY, 0, kv_list()); verify_get_between_c(kv, "记!", MAX_KEY, 0, kv_list()); verify_get_between_c(kv, "ZZZ", "A", 0, kv_list()); verify_get_between_c(kv, MAX_KEY, MIN_KEY, 0, kv_list()); CLEAR_KV(kv); verify_get_between_c(kv, MIN_KEY, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBetweenTest2(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some keys are removed. * This test is using C-like API. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_between_c(kv, MIN_KEY, MAX_KEY, 0, kv_list()); /* insert bunch of keys */ add_ext_keys(kv); auto expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_between_c(kv, EMPTY_KEY, "zzz", 7, kv_sort(expected)); expected = kv_list{{"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}}; verify_get_between_c(kv, "ccc", "yyy", 3, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_between_c(kv, "a", "z", 7, kv_sort(expected)); expected = kv_list{{"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}}; verify_get_between_c(kv, "ddd", "yyy", 3, kv_sort(expected)); expected = kv_list{ {"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, }; verify_get_between_c(kv, "a", "rrr", 3, kv_sort(expected)); /* remove one key */ ASSERT_STATUS(kv.remove("sss"), status::OK); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_between_c(kv, "a", "z", 6, kv_sort(expected)); expected = kv_list{{"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}}; verify_get_between_c(kv, "aaa", "sss", 3, kv_sort(expected)); /* empty/wrong range */ verify_get_between_c(kv, "yyy", "z", 0, kv_list()); verify_get_between_c(kv, "yyyy", "z", 0, kv_list()); verify_get_between_c(kv, "zzz", "zzzz", 0, kv_list()); verify_get_between_c(kv, "z", "yyyy", 0, kv_list()); verify_get_between_c(kv, "z", "yyy", 0, kv_list()); verify_get_between_c(kv, MAX_KEY, MIN_KEY, 0, kv_list()); CLEAR_KV(kv); verify_get_between_c(kv, MIN_KEY, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBetweenRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_between(kv, MIN_KEY, "randtest", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_between(kv, MIN_KEY, MAX_KEY, i + 1, kv_sort(expected)); /* verifies elements above the first one (and below MAX_KEY) */ auto exp_sorted = kv_sort(expected); verify_get_between(kv, exp_sorted[0].first, MAX_KEY, i, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); /* verifies elements below the last one (and above MIN_KEY) */ verify_get_between(kv, MIN_KEY, exp_sorted[i].first, i, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (i > 1) { /* verifies elements between first and the last */ verify_get_between( kv, exp_sorted[0].first, exp_sorted[i].first, i - 1, kv_list(exp_sorted.begin() + 1, exp_sorted.end() - 1)); } if (exp_sorted.size() > 10) { /* verifies some elements in the middle */ verify_get_between( kv, exp_sorted[4].first, exp_sorted[exp_sorted.size() - 5].first, exp_sorted.size() - 10, kv_list(exp_sorted.begin() + 5, exp_sorted.end() - 5)); } } CLEAR_KV(kv); kv.close(); } static void GetBetweenIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_between returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_between(kv, "a_inc", MAX_KEY, 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_between(kv, MIN_KEY, MAX_KEY, i + 1, kv_sort(expected)); /* verifies elements above the first one (and below MAX_KEY) */ auto exp_sorted = kv_sort(expected); verify_get_between(kv, exp_sorted[0].first, MAX_KEY, i, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); /* verifies elements below the last one (and above MIN_KEY) */ verify_get_between(kv, MIN_KEY, exp_sorted[i].first, i, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (i > 10) { /* verifies some elements in the middle */ verify_get_between( kv, exp_sorted[4].first, exp_sorted[exp_sorted.size() - 5].first, i - 9, kv_list(exp_sorted.begin() + 5, exp_sorted.end() - 5)); } } /* start over with two initial keys */ CLEAR_KV(kv); const std::string mid_key = std::string(2, char(127)); ASSERT_STATUS(kv.put(mid_key, "init0"), status::OK); ASSERT_STATUS(kv.put(mid_key + mid_key, "init1"), status::OK); expected = kv_list{{mid_key, "init0"}, {mid_key + mid_key, "init1"}}; verify_get_between(kv, MIN_KEY, MAX_KEY, 2, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); keys_cnt = charset_size * max_key_len; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_between(kv, MIN_KEY, MAX_KEY, i + 3, kv_sort(expected)); /* verifies elements above the first one (and below MAX_KEY) */ auto exp_sorted = kv_sort(expected); verify_get_between(kv, exp_sorted[0].first, MAX_KEY, i + 2, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); /* verifies elements below the last one (and above MIN_KEY) */ verify_get_between(kv, MIN_KEY, exp_sorted[exp_sorted.size() - 1].first, i + 2, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (i > 10) { /* verifies some elements in the middle */ verify_get_between( kv, exp_sorted[4].first, exp_sorted[exp_sorted.size() - 5].first, i - 7, kv_list(exp_sorted.begin() + 5, exp_sorted.end() - 5)); } } CLEAR_KV(kv); kv.close(); } static void GetBetweenIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_between returns properly all * data. After initial part of the test, some keys are deleted and some new keys * are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_between(kv, "&Rev&", "~~~", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); size_t curr_iter = keys_cnt - i; /* verifies all elements */ verify_get_between(kv, MIN_KEY, MAX_KEY, curr_iter + 1, kv_sort(expected)); /* verifies elements above the first one (and below MAX_KEY) */ auto exp_sorted = kv_sort(expected); verify_get_between(kv, exp_sorted[0].first, MAX_KEY, curr_iter, kv_list(exp_sorted.begin() + 1, exp_sorted.end())); /* verifies elements below the last one (and above MIN_KEY) */ verify_get_between(kv, MIN_KEY, exp_sorted[curr_iter].first, curr_iter, kv_list(exp_sorted.begin(), exp_sorted.end() - 1)); if (exp_sorted.size() > 10) { /* verifies some elements in the middle */ verify_get_between( kv, exp_sorted[4].first, exp_sorted[exp_sorted.size() - 5].first, exp_sorted.size() - 10, kv_list(exp_sorted.begin() + 5, exp_sorted.end() - 5)); } } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies above 11-th element */ auto exp_sorted = kv_sort(expected); verify_get_between_c(kv, exp_sorted[10].first, MAX_KEY, keys_cnt - 11, kv_list(exp_sorted.begin() + 11, exp_sorted.end())); /* verifies below 19-th element */ verify_get_between_c(kv, MIN_KEY, exp_sorted[18].first, 18, kv_list(exp_sorted.begin(), exp_sorted.begin() + 18)); /* verifies between 11-th and 19-th elements */ verify_get_between_c(kv, exp_sorted[10].first, exp_sorted[18].first, 7, kv_list(exp_sorted.begin() + 11, exp_sorted.begin() + 18)); /* verifies all elements */ verify_get_between_c(kv, MIN_KEY, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_between_c(kv, MIN_KEY, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_between_c(kv, MIN_KEY, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_between_c(kv, MIN_KEY, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_between_c(kv, MIN_KEY, MAX_KEY, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config items max_key_len", argv[0]); auto engine = std::string(argv[1]); size_t items = std::stoull(argv[3]); size_t max_key_len = std::stoull(argv[4]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); GetBetweenTest(engine, CONFIG_FROM_JSON(argv[2])); GetBetweenTest2(engine, CONFIG_FROM_JSON(argv[2])); GetBetweenRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetBetweenIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetBetweenIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/get_equal_above_gen_params.cc000066400000000000000000000257051410000423300266120ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" /** * Basic + generated tests for get_equal_above and count_equal_above methods for * sorted engines. get_equal_above method returns all elements in db with keys greater * than or equal to the given key (count returns the number of such records). */ static void GetEqualAboveTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_above(kv, EMPTY_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_equal_above(kv, EMPTY_KEY, 6, kv_sort(expected)); expected = kv_list{{"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_equal_above(kv, "B", 3, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_above(kv, "B", 4, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_above(kv, EMPTY_KEY, 7, kv_sort(expected)); verify_get_equal_above(kv, "ZZZ", 0, kv_list()); expected = kv_list{{"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_above(kv, "AZ", 4, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); /* testing C-like API */ expected = kv_list{{"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}, {"记!", "RR"}}; verify_get_equal_above_c(kv, "B", 5, kv_sort(expected)); expected = kv_list{{"记!", "RR"}}; verify_get_equal_above_c(kv, "记!", 1, kv_sort(expected)); CLEAR_KV(kv); verify_get_equal_above_c(kv, MIN_KEY, 0, kv_list()); kv.close(); } static void GetEqualAboveTest2(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some keys are removed. * This test is using C-like API. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_above_c(kv, MIN_KEY, 0, kv_list()); /* insert bunch of keys */ add_ext_keys(kv); auto expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, MIN_KEY, 7, kv_sort(expected)); expected = kv_list{ {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, "ccc", 5, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, "a", 7, kv_sort(expected)); expected = kv_list{{"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, "ddd", 4, kv_sort(expected)); /* remove one key */ ASSERT_STATUS(kv.remove("sss"), status::OK); expected = kv_list{{"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, "ddd", 3, kv_sort(expected)); expected = kv_list{{"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_above_c(kv, "sss", 2, kv_sort(expected)); verify_get_equal_above_c(kv, "z", 0, kv_list()); CLEAR_KV(kv); verify_get_equal_above_c(kv, MIN_KEY, 0, kv_list()); kv.close(); } static void GetEqualAboveRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_above(kv, "randtest", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_equal_above(kv, MIN_KEY, i + 1, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_above(kv, exp_sorted[0].first, i + 1, exp_sorted); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2; verify_get_equal_above( kv, exp_sorted[half - 1].first, exp_sorted.size() - half + 1, kv_list(exp_sorted.begin() + half - 1, exp_sorted.end())); } if (exp_sorted.size() > 5) { /* verifies last few elements */ verify_get_equal_above( kv, exp_sorted[exp_sorted.size() - 5].first, 5, kv_list(exp_sorted.end() - 5, exp_sorted.end())); } } CLEAR_KV(kv); kv.close(); } static void GetEqualAboveIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_equal_above returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_above(kv, "a_inc", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_equal_above(kv, MIN_KEY, i + 1, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_above(kv, exp_sorted[0].first, i + 1, exp_sorted); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2; verify_get_equal_above( kv, exp_sorted[half - 1].first, exp_sorted.size() - half + 1, kv_list(exp_sorted.begin() + half - 1, exp_sorted.end())); } } /* start over with two initial keys */ CLEAR_KV(kv); ASSERT_STATUS(kv.put(MAX_KEY, "init0"), status::OK); ASSERT_STATUS(kv.put(MAX_KEY + MAX_KEY, "init1"), status::OK); expected = kv_list{{MAX_KEY, "init0"}, {MAX_KEY + MAX_KEY, "init1"}}; verify_get_equal_above(kv, MIN_KEY, 2, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); keys_cnt = charset_size * max_key_len; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); if (i % 5 == 0) { /* verifies all elements */ verify_get_equal_above(kv, MIN_KEY, i + 3, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_above(kv, exp_sorted[0].first, i + 3, exp_sorted); } } CLEAR_KV(kv); kv.close(); } static void GetEqualAboveIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated incremented keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_equal_above returns properly * all data. After initial part of the test, some keys are deleted and some new * keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_above(kv, "&Rev&", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_equal_above(kv, MIN_KEY, keys_cnt - i + 1, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_above(kv, exp_sorted[0].first, keys_cnt - i + 1, exp_sorted); } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies equal_above 11-th element */ auto exp_sorted = kv_sort(expected); verify_get_equal_above_c(kv, exp_sorted[10].first, keys_cnt - 10, kv_list(exp_sorted.begin() + 10, exp_sorted.end())); /* verifies all elements */ verify_get_equal_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); /* remove 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_equal_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); /* remove 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_equal_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_equal_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_equal_above_c(kv, MIN_KEY, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config items max_key_len", argv[0]); auto engine = std::string(argv[1]); size_t items = std::stoull(argv[3]); size_t max_key_len = std::stoull(argv[4]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); GetEqualAboveTest(engine, CONFIG_FROM_JSON(argv[2])); GetEqualAboveTest2(engine, CONFIG_FROM_JSON(argv[2])); GetEqualAboveRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetEqualAboveIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetEqualAboveIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/get_equal_below_gen_params.cc000066400000000000000000000266451410000423300266320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" #include /** * Generated tests for get_equal_equal_below and count_equal_equal_below methods for * sorted engines. get_equal_below method returns all elements in db with keys lesser * than or equal to the given key (count returns the number of such records). */ static void GetBelowTest(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some new keys added. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_below(kv, EMPTY_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_equal_below(kv, MAX_KEY, 6, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}}; verify_get_equal_below(kv, "B", 4, kv_sort(expected)); expected = kv_list{{"A", "1"}}; verify_get_equal_below(kv, "AA", 1, kv_sort(expected)); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}}; verify_get_equal_below(kv, "B", 4, kv_sort(expected)); verify_get_equal_below(kv, EMPTY_KEY, 0, kv_list()); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_below(kv, "ZZZ", 7, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_below(kv, "BD", 7, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_below(kv, "ZZZ", 7, kv_sort(expected)); expected.emplace_back("记!", "RR"); verify_get_equal_below(kv, MAX_KEY, 8, kv_sort(expected)); /* testing C-like API */ verify_get_equal_below_c(kv, "记!", 8, kv_sort(expected)); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"BD", "7"}}; verify_get_equal_below_c(kv, "BE", 7, kv_sort(expected)); CLEAR_KV(kv); verify_get_equal_below_c(kv, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBelowTest2(std::string engine, pmem::kv::config &&config) { /** * TEST: Basic test with hardcoded strings. Some keys are removed. * This test is using C-like API. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_below_c(kv, MAX_KEY, 0, kv_list()); /* insert bunch of keys */ add_ext_keys(kv); auto expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"sss", "5"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_below_c(kv, MAX_KEY, 7, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}}; verify_get_equal_below_c(kv, "ccc", 3, kv_sort(expected)); verify_get_equal_below_c(kv, "a", 0, kv_list()); verify_get_equal_below_c(kv, EMPTY_KEY, 0, kv_list()); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}}; verify_get_equal_below_c(kv, "ddd", 3, kv_sort(expected)); /* remove one key */ ASSERT_STATUS(kv.remove("sss"), status::OK); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}}; verify_get_equal_below_c(kv, "sss", 4, kv_sort(expected)); expected = kv_list{{"aaa", "1"}, {"bbb", "2"}, {"ccc", "3"}, {"rrr", "4"}, {"ttt", "6"}, {"yyy", "记!"}}; verify_get_equal_below_c(kv, MAX_KEY, 6, kv_sort(expected)); verify_get_equal_below_c(kv, "z", 6, kv_sort(expected)); CLEAR_KV(kv); verify_get_equal_below_c(kv, MAX_KEY, 0, kv_list()); kv.close(); } static void GetBelowRandTest(std::string engine, pmem::kv::config &&config, const size_t items, const size_t max_key_len) { /** * TEST: Randomly generated keys. */ /* XXX: add comparator to kv_sort method, perhaps as param */ /* XXX: to be enabled for Comparator support (in all below test functions) */ // auto cmp = std::unique_ptr(new Comparator()); // auto s = config.put_comparator(std::move(cmp)); // ASSERT_STATUS(s, status::OK); auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_below(kv, "randtest", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_rand_keys(items, max_key_len); auto expected = kv_list(); std::string key, value; for (size_t i = 0; i < items; i++) { value = std::to_string(i); key = keys[i]; ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_equal_below(kv, MAX_KEY, i + 1, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_below(kv, exp_sorted[i].first, i + 1, kv_list(exp_sorted.begin(), exp_sorted.end())); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2 + 1; verify_get_equal_below( kv, exp_sorted[half - 1].first, half, kv_list(exp_sorted.begin(), exp_sorted.begin() + half)); } if (exp_sorted.size() > 5) { /* verifies first few elements */ verify_get_equal_below( kv, exp_sorted[4].first, 5, kv_list(exp_sorted.begin(), exp_sorted.begin() + 5)); } } CLEAR_KV(kv); kv.close(); } static void GetBelowIncrTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated keys with incremental keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added and checked if get_equal_below returns properly all data. * After initial part of the test, some new keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_below(kv, "a_inc", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); /* verifies all elements */ verify_get_equal_below(kv, MAX_KEY, i + 1, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_below(kv, exp_sorted[i].first, i + 1, kv_list(exp_sorted.begin(), exp_sorted.end())); if (exp_sorted.size() > 1) { /* verifies half of elements */ unsigned half = exp_sorted.size() / 2 + 1; verify_get_equal_below( kv, exp_sorted[half - 1].first, half, kv_list(exp_sorted.begin(), exp_sorted.begin() + half)); } } /* start over with two initial keys */ CLEAR_KV(kv); ASSERT_STATUS(kv.put(MIN_KEY, "init0"), status::OK); ASSERT_STATUS(kv.put(MIN_KEY + MIN_KEY, "init1"), status::OK); expected = kv_list{{MIN_KEY, "init0"}, {MIN_KEY + MIN_KEY, "init1"}}; verify_get_equal_below(kv, MAX_KEY, 2, kv_sort(expected)); /* add keys again */ keys = gen_incr_keys(max_key_len); keys_cnt = charset_size * max_key_len; for (size_t i = 0; i < keys_cnt; i++) { key = keys[i]; value = std::to_string(i); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); if (i % 5 == 0) { /* verifies all elements */ verify_get_equal_below(kv, MAX_KEY, i + 3, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_below( kv, exp_sorted[i + 2].first, i + 3, kv_list(exp_sorted.begin(), exp_sorted.end())); } } CLEAR_KV(kv); kv.close(); } static void GetBelowIncrReverseTest(std::string engine, pmem::kv::config &&config, const size_t max_key_len) { /** * TEST: Generated keys with incremental keys, e.g. "A", "AA", ..., "B", "BB", ... * Keys are added in reverse order and checked if get_equal_below returns properly * all data. After initial part of the test, some keys are deleted and some new * keys are added. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_equal_below(kv, "&Rev&", 0, kv_list()); /* generate keys and put them one at a time */ std::vector keys = gen_incr_keys(max_key_len); auto expected = kv_list(); size_t keys_cnt = charset_size * max_key_len; std::string key, value; for (size_t i = keys_cnt; i > 0; i--) { key = keys[i - 1]; value = std::to_string(i - 1); ASSERT_STATUS(kv.put(key, value), status::OK); expected.emplace_back(key, value); size_t curr_iter = keys_cnt - i + 1; /* verifies all elements */ verify_get_equal_below(kv, MAX_KEY, curr_iter, kv_sort(expected)); /* verifies all elements */ auto exp_sorted = kv_sort(expected); verify_get_equal_below(kv, exp_sorted[curr_iter - 1].first, curr_iter, kv_list(exp_sorted.begin(), exp_sorted.end())); } /* delete some keys, add some new keys and check again (using C-like API) */ /* remove 19th key */ UT_ASSERT(keys_cnt > 20); key = keys[19]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies equal_below 19-th element */ auto exp_sorted = kv_sort(expected); verify_get_equal_below_c(kv, exp_sorted[18].first, 19, kv_list(exp_sorted.begin(), exp_sorted.begin() + 19)); /* verifies all elements */ verify_get_equal_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 9th key */ UT_ASSERT(keys_cnt > 9); key = keys[8]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_equal_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); /* remove 3rd key */ UT_ASSERT(keys_cnt > 3); key = keys[2]; ASSERT_STATUS(kv.get(key, &value), status::OK); ASSERT_STATUS(kv.remove(key), status::OK); expected.erase(std::remove(expected.begin(), expected.end(), kv_pair{key, value}), expected.end()); keys_cnt--; /* verifies all elements */ verify_get_equal_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("!@", "!@"), status::OK); expected.emplace_back("!@", "!@"); keys_cnt++; verify_get_equal_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); ASSERT_STATUS(kv.put("", ""), status::OK); expected.emplace_back("", ""); keys_cnt++; verify_get_equal_below_c(kv, MAX_KEY, keys_cnt, kv_sort(expected)); CLEAR_KV(kv); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config items max_key_len", argv[0]); auto engine = std::string(argv[1]); size_t items = std::stoull(argv[3]); size_t max_key_len = std::stoull(argv[4]); auto seed = unsigned(std::time(0)); printf("rand seed: %u\n", seed); std::srand(seed); GetBelowTest(engine, CONFIG_FROM_JSON(argv[2])); GetBelowTest2(engine, CONFIG_FROM_JSON(argv[2])); GetBelowRandTest(engine, CONFIG_FROM_JSON(argv[2]), items, max_key_len); GetBelowIncrTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); GetBelowIncrReverseTest(engine, CONFIG_FROM_JSON(argv[2]), max_key_len); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/iterate.cc000066400000000000000000000160701410000423300227240ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "iterate.hpp" /** * Common tests for all count_* and get_* methods for sorted engines. */ static void CommonCountTest(std::string engine, pmem::kv::config &&config) { /** * TEST: mix of all count_* methods with hardcoded strings. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); /* insert bunch of keys */ add_basic_keys(kv); std::size_t cnt; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 6); /* insert new key */ ASSERT_STATUS(kv.put("BD", "7"), status::OK); ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == 7); cnt = std::numeric_limits::max(); /* mixed count functions checked with empty key */ ASSERT_STATUS(kv.count_above(EMPTY_KEY, cnt), status::OK); UT_ASSERT(cnt == 7); cnt = 0; ASSERT_STATUS(kv.count_equal_above(EMPTY_KEY, cnt), status::OK); UT_ASSERT(cnt == 7); ASSERT_STATUS(kv.count_below(EMPTY_KEY, cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 256; ASSERT_STATUS(kv.count_equal_below(EMPTY_KEY, cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.count_between(EMPTY_KEY, "ZZZZ", cnt), status::OK); UT_ASSERT(cnt == 7); cnt = 10000; ASSERT_STATUS(kv.count_between(EMPTY_KEY, MAX_KEY, cnt), status::OK); UT_ASSERT(cnt == 7); /* group of check for each count_* function, tested with various keys */ ASSERT_STATUS(kv.count_above("A", cnt), status::OK); UT_ASSERT(cnt == 6); ASSERT_STATUS(kv.count_above("B", cnt), status::OK); UT_ASSERT(cnt == 3); ASSERT_STATUS(kv.count_above("BC", cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.count_above("BD", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1; ASSERT_STATUS(kv.count_above("ZZ", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 0; ASSERT_STATUS(kv.count_equal_above("A", cnt), status::OK); UT_ASSERT(cnt == 7); ASSERT_STATUS(kv.count_equal_above("AA", cnt), status::OK); UT_ASSERT(cnt == 6); ASSERT_STATUS(kv.count_equal_above("B", cnt), status::OK); UT_ASSERT(cnt == 4); ASSERT_STATUS(kv.count_equal_above("BC", cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.count_equal_above("BD", cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.count_equal_above("Z", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 10; ASSERT_STATUS(kv.count_below("A", cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.count_below("B", cnt), status::OK); UT_ASSERT(cnt == 3); ASSERT_STATUS(kv.count_below("BC", cnt), status::OK); UT_ASSERT(cnt == 5); ASSERT_STATUS(kv.count_below("BD", cnt), status::OK); UT_ASSERT(cnt == 6); ASSERT_STATUS(kv.count_below("ZZZZZ", cnt), status::OK); UT_ASSERT(cnt == 7); ASSERT_STATUS(kv.count_equal_below("A", cnt), status::OK); UT_ASSERT(cnt == 1); ASSERT_STATUS(kv.count_equal_below("B", cnt), status::OK); UT_ASSERT(cnt == 4); cnt = 257; ASSERT_STATUS(kv.count_equal_below("BA", cnt), status::OK); UT_ASSERT(cnt == 4); ASSERT_STATUS(kv.count_equal_below("BC", cnt), status::OK); UT_ASSERT(cnt == 6); ASSERT_STATUS(kv.count_equal_below("BD", cnt), status::OK); UT_ASSERT(cnt == 7); cnt = 258; ASSERT_STATUS(kv.count_equal_below("ZZZZZZ", cnt), status::OK); UT_ASSERT(cnt == 7); cnt = 1024; ASSERT_STATUS(kv.count_between(EMPTY_KEY, "A", cnt), status::OK); UT_ASSERT(cnt == 0); ASSERT_STATUS(kv.count_between(EMPTY_KEY, "B", cnt), status::OK); UT_ASSERT(cnt == 3); ASSERT_STATUS(kv.count_between("A", "B", cnt), status::OK); UT_ASSERT(cnt == 2); ASSERT_STATUS(kv.count_between("A", "BD", cnt), status::OK); UT_ASSERT(cnt == 5); ASSERT_STATUS(kv.count_between("B", "ZZ", cnt), status::OK); UT_ASSERT(cnt == 3); cnt = 1024; ASSERT_STATUS(kv.count_between(EMPTY_KEY, EMPTY_KEY, cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1025; ASSERT_STATUS(kv.count_between("A", "A", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1026; ASSERT_STATUS(kv.count_between("AC", "A", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1027; ASSERT_STATUS(kv.count_between("B", "A", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1028; ASSERT_STATUS(kv.count_between("BD", "A", cnt), status::OK); UT_ASSERT(cnt == 0); cnt = 1029; ASSERT_STATUS(kv.count_between("ZZZ", "B", cnt), status::OK); UT_ASSERT(cnt == 0); CLEAR_KV(kv); kv.close(); } static void CommonGetTest(std::string engine, pmem::kv::config &&config) { /** * TEST: mix of all get_* and count_* methods with hardcoded strings. * It's NOT suitable to test with custom comparator. */ auto kv = INITIALIZE_KV(engine, std::move(config)); verify_get_all(kv, 0, kv_list()); verify_get_above(kv, EMPTY_KEY, 0, kv_list()); verify_get_below(kv, EMPTY_KEY, 0, kv_list()); verify_get_between(kv, MIN_KEY, MAX_KEY, 0, kv_list()); verify_get_equal_above(kv, EMPTY_KEY, 0, kv_list()); verify_get_equal_below(kv, EMPTY_KEY, 0, kv_list()); /* insert bunch of keys */ add_basic_keys(kv); auto expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_below(kv, MAX_KEY, 6, kv_sort(expected)); verify_get_equal_above(kv, EMPTY_KEY, 6, kv_sort(expected)); verify_get_all(kv, 6, kv_sort(expected)); verify_get_above(kv, EMPTY_KEY, 6, kv_sort(expected)); verify_get_equal_below(kv, MAX_KEY, 6, kv_sort(expected)); verify_get_between(kv, EMPTY_KEY, MAX_KEY, 6, kv_sort(expected)); /* insert new key with special char in key */ ASSERT_STATUS(kv.put("记!", "RR"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}, {"记!", "RR"}}; verify_get_between(kv, EMPTY_KEY, MAX_KEY, expected.size(), kv_sort(expected)); verify_get_equal_above(kv, EMPTY_KEY, expected.size(), kv_sort(expected)); verify_get_above(kv, EMPTY_KEY, expected.size(), kv_sort(expected)); verify_get_below(kv, MAX_KEY, expected.size(), kv_sort(expected)); verify_get_equal_below(kv, MAX_KEY, expected.size(), kv_sort(expected)); verify_get_all(kv, expected.size(), kv_sort(expected)); /* remove the new key */ ASSERT_STATUS(kv.remove("记!"), status::OK); expected = kv_list{{"A", "1"}, {"AB", "2"}, {"AC", "3"}, {"B", "4"}, {"BB", "5"}, {"BC", "6"}}; verify_get_below(kv, "Z", 6, kv_sort(expected)); verify_get_between(kv, EMPTY_KEY, "Z", 6, kv_sort(expected)); verify_get_equal_below(kv, "Z", 6, kv_sort(expected)); verify_get_above(kv, EMPTY_KEY, 6, kv_sort(expected)); verify_get_all(kv, 6, kv_sort(expected)); verify_get_equal_above(kv, EMPTY_KEY, 6, kv_sort(expected)); CLEAR_KV(kv); verify_get_equal_above(kv, EMPTY_KEY, 0, kv_list()); verify_get_all(kv, 0, kv_list()); verify_get_above(kv, EMPTY_KEY, 0, kv_list()); verify_get_equal_below(kv, EMPTY_KEY, 0, kv_list()); verify_get_between(kv, MIN_KEY, MAX_KEY, 0, kv_list()); verify_get_below(kv, EMPTY_KEY, 0, kv_list()); kv.close(); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); auto engine = std::string(argv[1]); CommonCountTest(engine, CONFIG_FROM_JSON(argv[2])); CommonGetTest(engine, CONFIG_FROM_JSON(argv[2])); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/sorted/iterate.hpp000066400000000000000000000255241410000423300231320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020-2021, Intel Corporation */ #include "unittest.hpp" #include #include /** * Helper class for testing get_* and count_* functions */ using namespace pmem::kv; using kv_pair = std::pair; using kv_list = std::vector; const std::string EMPTY_KEY = ""; const std::string MIN_KEY = std::string(1, char(1)); const std::string MAX_KEY = std::string(4, char(255)); inline kv_list kv_sort(kv_list list) { std::sort(list.begin(), list.end(), [](const kv_pair &lhs, const kv_pair &rhs) { return lhs.first < rhs.first; }); return list; } #define KV_GET_ALL_CPP_CB_LST(list) \ kv.get_all([&](string_view k, string_view v) { \ list.emplace_back(std::string(k.data(), k.size()), \ std::string(v.data(), v.size())); \ return 0; \ }); #define KV_GET_ALL_C_CB_LST(list) \ kv.get_all( \ [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { \ const auto c = ((kv_list *)arg); \ c->emplace_back(std::string(k, kb), std::string(v, vb)); \ return 0; \ }, \ &list) #define KV_GET_1KEY_CPP_CB_LST(func, key, list) \ kv.func(key, [&](string_view k, string_view v) { \ list.emplace_back(std::string(k.data(), k.size()), \ std::string(v.data(), v.size())); \ return 0; \ }) #define KV_GET_1KEY_C_CB_LST(func, key, list) \ kv.func( \ key, \ [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { \ const auto c = ((kv_list *)arg); \ c->emplace_back(std::string(k, kb), std::string(v, vb)); \ return 0; \ }, \ &list) #define KV_GET_2KEYS_CPP_CB_LST(func, key1, key2, list) \ kv.func(key1, key2, [&](string_view k, string_view v) { \ list.emplace_back(std::string(k.data(), k.size()), \ std::string(v.data(), v.size())); \ return 0; \ }) #define KV_GET_2KEYS_C_CB_LST(func, key1, key2, list) \ kv.func( \ key1, key2, \ [](const char *k, size_t kb, const char *v, size_t vb, void *arg) { \ const auto c = ((kv_list *)arg); \ c->emplace_back(std::string(k, kb), std::string(v, vb)); \ return 0; \ }, \ &list) inline void verify_get_all(pmem::kv::db &kv, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_ALL_CPP_CB_LST(result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_all_c(pmem::kv::db &kv, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_ALL_C_CB_LST(result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_above(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_above(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_CPP_CB_LST(get_above, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_above_c(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_above(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_C_CB_LST(get_above, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_equal_above(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_equal_above(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_CPP_CB_LST(get_equal_above, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_equal_above_c(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_equal_above(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_C_CB_LST(get_equal_above, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_below(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_below(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_CPP_CB_LST(get_below, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_below_c(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_below(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_C_CB_LST(get_below, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_equal_below(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_equal_below(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_CPP_CB_LST(get_equal_below, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_equal_below_c(pmem::kv::db &kv, const std::string key, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_equal_below(key, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_1KEY_C_CB_LST(get_equal_below, key, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_between(pmem::kv::db &kv, const std::string key1, const std::string key2, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_between(key1, key2, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_2KEYS_CPP_CB_LST(get_between, key1, key2, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } inline void verify_get_between_c(pmem::kv::db &kv, const std::string key1, const std::string key2, const size_t exp_cnt, const kv_list &sorted_exp_result) { std::size_t cnt; kv_list result; ASSERT_STATUS(kv.count_between(key1, key2, cnt), status::OK); UT_ASSERT(cnt == exp_cnt); auto s = KV_GET_2KEYS_C_CB_LST(get_between, key1, key2, result); ASSERT_STATUS(s, status::OK); UT_ASSERT(result == sorted_exp_result); } const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; const size_t charset_size = strlen(charset); inline void add_basic_keys(pmem::kv::db &kv) { ASSERT_STATUS(kv.put("A", "1"), status::OK); ASSERT_STATUS(kv.put("AB", "2"), status::OK); ASSERT_STATUS(kv.put("AC", "3"), status::OK); ASSERT_STATUS(kv.put("B", "4"), status::OK); ASSERT_STATUS(kv.put("BB", "5"), status::OK); ASSERT_STATUS(kv.put("BC", "6"), status::OK); } inline void add_ext_keys(pmem::kv::db &kv) { ASSERT_STATUS(kv.put("aaa", "1"), status::OK); ASSERT_STATUS(kv.put("bbb", "2"), status::OK); ASSERT_STATUS(kv.put("ccc", "3"), status::OK); ASSERT_STATUS(kv.put("rrr", "4"), status::OK); ASSERT_STATUS(kv.put("sss", "5"), status::OK); ASSERT_STATUS(kv.put("ttt", "6"), status::OK); ASSERT_STATUS(kv.put("yyy", "记!"), status::OK); } /* * Generates incremental keys using each char from global variable charset. * List looks e.g. like: "A", "AA", ..., "B", "BB", ... */ inline std::vector gen_incr_keys(const size_t max_key_len) { std::vector keys; for (size_t i = 0; i < charset_size; i++) { std::string key = ""; char curr_char = charset[i]; for (size_t j = 0; j < max_key_len; j++) { key += curr_char; keys.push_back(key); } } return keys; } /* generates 'cnt' unique keys with random content, using global variable charset */ inline std::vector gen_rand_keys(const size_t cnt, const size_t max_key_len) { std::vector keys; std::string gen_key; for (size_t k = 0; k < cnt; k++) { do { /* various lengths of key, min: 2 */ size_t key_len = 2 + ((size_t)rand() % (max_key_len - 1)); gen_key.clear(); gen_key.reserve(key_len); for (size_t i = 0; i < key_len; i++) { gen_key.push_back(charset[(size_t)rand() % charset_size]); } } while (std::find(keys.begin(), keys.end(), gen_key) != keys.end()); keys.push_back(std::move(gen_key)); } return keys; } /* Custom comparator */ class reverse_comparator { public: reverse_comparator() : runtime_data(new int(RUNTIME_DATA_VAL)) { } reverse_comparator(reverse_comparator &&cmp) { this->runtime_data = cmp.runtime_data; cmp.runtime_data = nullptr; } reverse_comparator(const reverse_comparator &cmp) = delete; reverse_comparator &operator=(const reverse_comparator &cmp) = delete; ~reverse_comparator() { delete runtime_data; } int compare(string_view key1, string_view key2) const { UT_ASSERT(*runtime_data == RUNTIME_DATA_VAL); return key2.compare(key1); } std::string name() { return "reverse_comparator"; } private: static constexpr int RUNTIME_DATA_VAL = 0xABC; int *runtime_data; }; pmemkv-1.5.0/tests/engine_scenarios/sorted/iterator_sorted.cc000066400000000000000000000163421410000423300245020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../iterator.hpp" /** * Test methods available only in sorted engines' iterators. */ template static void seek_lower_test(pmem::kv::db &kv) { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_lower(p.first), pmem::kv::status::NOT_FOUND); }); insert_keys(kv); ASSERT_STATUS(it.seek_lower(keys[0].first), pmem::kv::status::NOT_FOUND); auto prev_key = keys.begin(); std::for_each(keys.begin() + 1, keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_lower(p.first), pmem::kv::status::OK); verify_key(it, prev_key->first); verify_value(it, (prev_key++)->second); }); } template static void seek_lower_eq_test(pmem::kv::db &kv) { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_lower_eq(p.first), pmem::kv::status::NOT_FOUND); }); insert_keys(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_lower_eq(p.first), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); }); /* check with not equal elements */ std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_lower_eq(p.first + "aa"), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); }); } template static void seek_higher_test(pmem::kv::db &kv) { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_higher(p.first), pmem::kv::status::NOT_FOUND); }); insert_keys(kv); auto next_key = keys.begin() + 1; std::for_each(keys.begin(), keys.end() - 1, [&](pair p) { ASSERT_STATUS(it.seek_higher(p.first), pmem::kv::status::OK); verify_key(it, next_key->first); verify_value(it, (next_key++)->second); }); ASSERT_STATUS(it.seek_higher(keys[keys.size() - 1].first), pmem::kv::status::NOT_FOUND); } template static void seek_higher_eq_test(pmem::kv::db &kv) { auto it = new_iterator(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_higher_eq(p.first), pmem::kv::status::NOT_FOUND); }); insert_keys(kv); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek_higher_eq(p.first), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); }); /* check with not equal elements */ auto prev = std::string("aa"); std::for_each(keys.begin(), keys.end() - 1, [&](pair p) { ASSERT_STATUS(it.seek_higher_eq(prev), pmem::kv::status::OK); verify_key(it, p.first); verify_value(it, p.second); prev = p.first + "aa"; }); } template static void next_test(pmem::kv::db &kv) { auto it = new_iterator(kv); insert_keys(kv); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); std::for_each(keys.begin(), keys.end() - 1, [&](pair p) { verify_key(it, p.first); verify_value(it, p.second); ASSERT_STATUS(it.is_next(), pmem::kv::status::OK); ASSERT_STATUS(it.next(), pmem::kv::status::OK); }); verify_key(it, (--keys.end())->first); verify_value(it, (--keys.end())->second); ASSERT_STATUS(it.is_next(), pmem::kv::status::NOT_FOUND); ASSERT_STATUS(it.next(), pmem::kv::status::NOT_FOUND); } template static void prev_test(pmem::kv::db &kv) { auto it = new_iterator(kv); insert_keys(kv); ASSERT_STATUS(it.seek_to_last(), pmem::kv::status::OK); std::for_each(keys.rbegin(), keys.rend() - 1, [&](pair p) { verify_key(it, p.first); verify_value(it, p.second); ASSERT_STATUS(it.prev(), pmem::kv::status::OK); }); verify_key(it, keys.begin()->first); verify_value(it, keys.begin()->second); ASSERT_STATUS(it.prev(), pmem::kv::status::NOT_FOUND); } template static void seek_to_first_test(pmem::kv::db &kv) { auto it = new_iterator(kv); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::NOT_FOUND); insert_keys(kv); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); auto first_key = keys.begin(); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::OK); ASSERT_STATUS(it.seek_to_first(), pmem::kv::status::OK); verify_key(it, first_key->first); verify_value(it, first_key->second); }); } template static void seek_to_last_test(pmem::kv::db &kv) { auto it = new_iterator(kv); ASSERT_STATUS(it.seek_to_last(), pmem::kv::status::NOT_FOUND); insert_keys(kv); ASSERT_STATUS(it.seek_to_last(), pmem::kv::status::OK); auto last_key = --keys.end(); std::for_each(keys.begin(), keys.end(), [&](pair p) { ASSERT_STATUS(it.seek(p.first), pmem::kv::status::OK); ASSERT_STATUS(it.seek_to_last(), pmem::kv::status::OK); verify_key(it, last_key->first); verify_value(it, last_key->second); }); } static void seek_to_first_write_test(pmem::kv::db &kv) { auto it = new_iterator(kv); insert_keys(kv); /* check if seek_to_first() will internally abort transaction */ it.seek(keys.back().first); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; it.seek_to_first(); it.commit(); verify_keys(it); /* write something after seek_to_first() */ it.seek_to_first(); auto res2 = it.write_range(); UT_ASSERT(res2.is_ok()); for (auto &c : res2.get_value()) c = 'o'; it.commit(); verify_value(it, std::string(keys.front().second.size(), 'o')); } static void seek_to_last_write_test(pmem::kv::db &kv) { auto it = new_iterator(kv); insert_keys(kv); /* check if seek_to_last will internally abort transaction */ it.seek(keys.front().first); auto res = it.write_range(); UT_ASSERT(res.is_ok()); for (auto &c : res.get_value()) c = 'a'; it.seek_to_last(); it.commit(); verify_keys(it); /* write something after seek_to_last() */ it.seek_to_last(); auto res2 = it.write_range(); UT_ASSERT(res2.is_ok()); for (auto &c : res2.get_value()) c = 'o'; it.commit(); verify_value(it, std::string(keys.back().second.size(), 'o')); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config [if_test_prev]", argv[0]); run_engine_tests(argv[1], argv[2], { seek_lower_test, seek_lower_test, seek_lower_eq_test, seek_lower_eq_test, seek_higher_test, seek_higher_test, seek_higher_eq_test, seek_higher_eq_test, next_test, next_test, seek_to_first_test, seek_to_first_test, seek_to_first_write_test, }); /* check if iterator supports prev and seek_to_last methods */ if (argc < 4 || std::string(argv[3]).compare("false") != 0) run_engine_tests(argv[1], argv[2], { prev_test, prev_test, seek_to_last_test, seek_to_last_test, seek_to_last_write_test, }); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/transaction/000077500000000000000000000000001410000423300220015ustar00rootroot00000000000000pmemkv-1.5.0/tests/engine_scenarios/transaction/not_supported.cc000066400000000000000000000010551410000423300252160ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include "unittest.hpp" using namespace pmem::kv; static void test_tx_status(pmem::kv::db &kv) { auto tx = kv.tx_begin(); UT_ASSERT(!tx.is_ok()); auto s = tx.get_status(); ASSERT_STATUS(s, status::NOT_SUPPORTED); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], {test_tx_status}); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/transaction/put.cc000066400000000000000000000106231410000423300231220ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" #include "unittest.hpp" const size_t N_INSERTS = 10; const size_t KEY_LENGTH = 10; const size_t VALUE_LENGTH = 10; static void verify_not_found(const std::map &elements, pmem::kv::db &kv) { for (auto &e : elements) ASSERT_STATUS(kv.exists(e.first), pmem::kv::status::NOT_FOUND); } static void test_put_abort(pmem::kv::db &kv) { auto tx = kv.tx_begin().get_value(); auto proto = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, tx); verify_not_found(proto, kv); ASSERT_SIZE(kv, 0); tx.abort(); verify_not_found(proto, kv); } static void test_put_commit(pmem::kv::db &kv) { auto tx = kv.tx_begin().get_value(); auto proto = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, tx); verify_not_found(proto, kv); ASSERT_SIZE(kv, 0); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); VerifyKv(proto, kv); } static void test_put_destroy(pmem::kv::db &kv) { std::map proto; { auto tx = kv.tx_begin().get_value(); proto = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, tx); verify_not_found(proto, kv); ASSERT_SIZE(kv, 0); } verify_not_found(proto, kv); ASSERT_SIZE(kv, 0); } static void test_overwrite_commit(pmem::kv::db &kv) { /* Initialize kv */ auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); auto proto_tx = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH * 2, tx); VerifyKv(proto_kv, kv); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); VerifyKv(proto_tx, kv); } static void test_overwrite_abort(pmem::kv::db &kv) { /* Initialize kv */ auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); auto proto_tx = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH * 2, tx); VerifyKv(proto_kv, kv); tx.abort(); VerifyKv(proto_kv, kv); } static void test_use_after_commit(pmem::kv::db &kv) { auto tx = kv.tx_begin().get_value(); auto proto = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, tx); verify_not_found(proto, kv); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); /* Rollback changes */ for (auto &e : proto) ASSERT_STATUS(kv.remove(e.first), pmem::kv::status::OK); ASSERT_STATUS(tx.put("extra_key", "extra_value"), pmem::kv::status::OK); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); verify_not_found(proto, kv); ASSERT_STATUS(kv.exists("extra_key"), pmem::kv::status::OK); ASSERT_SIZE(kv, 1); } static void test_use_after_abort(pmem::kv::db &kv) { auto tx = kv.tx_begin().get_value(); auto proto = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, tx); verify_not_found(proto, kv); tx.abort(); ASSERT_STATUS(tx.put("extra_key", "extra_value"), pmem::kv::status::OK); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); verify_not_found(proto, kv); ASSERT_STATUS(kv.exists("extra_key"), pmem::kv::status::OK); ASSERT_SIZE(kv, 1); } static void test_batched_updates(pmem::kv::db &kv) { const int NUM_BATCH = 10000; const int BATCH_SIZE = 10; auto gen_key = [](int b, int i) { return std::to_string(b) + ";" + std::to_string(i) + std::string(40, 'X'); }; for (int i = 0; i < NUM_BATCH; i++) { auto tx = kv.tx_begin().get_value(); for (int j = 0; j < BATCH_SIZE; j++) { std::string key = gen_key(i, j); std::string value = key; ASSERT_STATUS(tx.put(key, value), pmem::kv::status::OK); ASSERT_STATUS(kv.exists(key), pmem::kv::status::NOT_FOUND); } ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); } size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERT(cnt == NUM_BATCH * BATCH_SIZE); for (int i = 0; i < NUM_BATCH; i++) { for (int j = 0; j < BATCH_SIZE; j++) { std::string key = gen_key(i, j); std::string val; ASSERT_STATUS(kv.get(key, &val), pmem::kv::status::OK); UT_ASSERT(val == key); } } } // XXX - add tests with UT_ASSERT(OID_IS_NULL(pmemobj_first(pop.handle()))); once // destroy() is implemented static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], {test_put_abort, test_put_commit, test_put_destroy, test_batched_updates, test_use_after_commit, test_use_after_abort, test_overwrite_abort, test_overwrite_commit}); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/transaction/put_pmreorder.cc000066400000000000000000000034501410000423300252010ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ /* * insert.cc -- insert pmreorder test */ #include "unittest.hpp" static std::vector init_elements = {"0", "1"}; static std::vector elements = {"A", "B", "C"}; static void test_put_commit(pmem::kv::db &kv) { auto tx = kv.tx_begin().get_value(); for (auto &e : elements) ASSERT_STATUS(tx.put(e, e), pmem::kv::status::OK); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); } static void test_init(pmem::kv::db &kv) { for (auto &e : init_elements) ASSERT_STATUS(kv.put(e, e), pmem::kv::status::OK); } static void check_consistency(pmem::kv::db &kv) { std::size_t size; ASSERT_STATUS(kv.count_all(size), pmem::kv::status::OK); for (auto &e : init_elements) ASSERT_STATUS(kv.exists(e), pmem::kv::status::OK); if (size > init_elements.size()) { UT_ASSERT(size == init_elements.size() + elements.size()); for (auto &e : elements) ASSERT_STATUS(kv.exists(e), pmem::kv::status::OK); } else { UT_ASSERT(size == init_elements.size()); } } static void test(int argc, char *argv[]) { std::cout << "ARGC: " << argc << std::endl; for (int i = 0; i < argc; ++i) { std::cout << "ARGV " << i << " : " << argv[i] << std::endl; } if (argc < 4) UT_FATAL("usage: %s engine json_config ", argv[0]); std::string mode = argv[3]; if (mode != "create" && mode != "open" && mode != "insert") UT_FATAL("usage: %s engine json_config ", argv[0]); auto kv = INITIALIZE_KV(argv[1], CONFIG_FROM_JSON(argv[2])); if (mode == "create") { test_init(kv); } else if (mode == "open") { check_consistency(kv); } else if (mode == "insert") { test_put_commit(kv); } kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engine_scenarios/transaction/remove.cc000066400000000000000000000140601410000423300236060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ #include "../put_get_std_map.hpp" #include "unittest.hpp" const size_t N_INSERTS = 10; const size_t KEY_LENGTH = 10; const size_t VALUE_LENGTH = 10; static void verify_not_found(const std::map &elements, pmem::kv::db &kv) { for (auto &e : elements) ASSERT_STATUS(kv.exists(e.first), pmem::kv::status::NOT_FOUND); } static void test_remove_commit(pmem::kv::db &kv) { auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (auto &e : proto_kv) ASSERT_STATUS(tx.remove(e.first), pmem::kv::status::OK); VerifyKv(proto_kv, kv); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); verify_not_found(proto_kv, kv); } static void test_remove_abort(pmem::kv::db &kv) { auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (auto &e : proto_kv) ASSERT_STATUS(tx.remove(e.first), pmem::kv::status::OK); VerifyKv(proto_kv, kv); tx.abort(); VerifyKv(proto_kv, kv); } static void test_remove_destroy(pmem::kv::db &kv) { std::map proto_kv; { proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (auto &e : proto_kv) ASSERT_STATUS(tx.remove(e.first), pmem::kv::status::OK); VerifyKv(proto_kv, kv); } VerifyKv(proto_kv, kv); } static void test_remove_inserted(pmem::kv::db &kv) { const int NUM_ITER = 100; auto gen_key = [](int i) { return "unique_prefix" + std::to_string(i); }; /* remove each inserted element */ { auto tx = kv.tx_begin().get_value(); for (int i = 0; i < NUM_ITER; i++) { auto e = gen_key(i); ASSERT_STATUS(tx.put(e, e), pmem::kv::status::OK); ASSERT_STATUS(tx.remove(e), pmem::kv::status::OK); } ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); std::size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, 0); } /* remove every second inserted element */ { auto tx = kv.tx_begin().get_value(); for (int i = 0; i < NUM_ITER; i++) { auto e = gen_key(i); ASSERT_STATUS(tx.put(e, e), pmem::kv::status::OK); if (i % 2 == 0) ASSERT_STATUS(tx.remove(e), pmem::kv::status::OK); } ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); std::size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, NUM_ITER / 2); } /* remove each inserted element but start with non-empty database */ { auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (int i = 0; i < NUM_ITER; i++) { auto e = gen_key(i); ASSERT_STATUS(tx.put(e, e), pmem::kv::status::OK); ASSERT_STATUS(tx.remove(e), pmem::kv::status::OK); } ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); std::size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERTeq(cnt, proto_kv.size()); } } static void test_put_and_remove(pmem::kv::db &kv) { const int NUM_BATCH = 10000; const int BATCH_SIZE = 10; auto gen_key = [](int b, int i) { return std::to_string(b) + ";" + std::to_string(i) + std::string(40, 'X'); }; for (int i = 0; i < NUM_BATCH; i++) { auto tx = kv.tx_begin().get_value(); for (int j = 0; j < BATCH_SIZE; j++) { std::string key = gen_key(i, j); std::string value = key; ASSERT_STATUS(kv.put(key, value), pmem::kv::status::OK); } /* remove half the elements inserted above and BATCH_SIZE of non-existent * elements (should have no effect) */ for (int j = BATCH_SIZE / 2; j < BATCH_SIZE + BATCH_SIZE / 2; j++) { ASSERT_STATUS(tx.remove(gen_key(i, j)), pmem::kv::status::OK); } ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); } std::size_t cnt; ASSERT_STATUS(kv.count_all(cnt), pmem::kv::status::OK); UT_ASSERT(cnt == NUM_BATCH * BATCH_SIZE / 2); for (int i = 0; i < NUM_BATCH; i++) { for (int j = 0; j < BATCH_SIZE / 2; j++) { std::string key = gen_key(i, j); ASSERT_STATUS(kv.exists(key), pmem::kv::status::OK); } } } static void test_use_after_commit(pmem::kv::db &kv) { auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (auto &e : proto_kv) ASSERT_STATUS(tx.remove(e.first), pmem::kv::status::OK); VerifyKv(proto_kv, kv); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); verify_not_found(proto_kv, kv); /* Rollback changes */ PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); ASSERT_STATUS(tx.put("extra_key", "extra_value"), pmem::kv::status::OK); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); for (const auto &record : proto_kv) { const auto &key = record.first; const auto &val = record.second; auto s = kv.get(key, [&](string_view value) { UT_ASSERT(value.compare(val) == 0); }); ASSERT_STATUS(s, status::OK); } ASSERT_STATUS(kv.exists("extra_key"), pmem::kv::status::OK); ASSERT_SIZE(kv, proto_kv.size() + 1); } static void test_use_after_abort(pmem::kv::db &kv) { auto proto_kv = PutToMapTest(N_INSERTS, KEY_LENGTH, VALUE_LENGTH, kv); auto tx = kv.tx_begin().get_value(); for (auto &e : proto_kv) ASSERT_STATUS(tx.remove(e.first), pmem::kv::status::OK); VerifyKv(proto_kv, kv); tx.abort(); ASSERT_STATUS(tx.put("extra_key", "extra_value"), pmem::kv::status::OK); ASSERT_STATUS(tx.commit(), pmem::kv::status::OK); for (const auto &record : proto_kv) { const auto &key = record.first; const auto &val = record.second; auto s = kv.get(key, [&](string_view value) { UT_ASSERT(value.compare(val) == 0); }); ASSERT_STATUS(s, status::OK); } ASSERT_STATUS(kv.exists("extra_key"), pmem::kv::status::OK); ASSERT_SIZE(kv, proto_kv.size() + 1); } static void test(int argc, char *argv[]) { if (argc < 3) UT_FATAL("usage: %s engine json_config", argv[0]); run_engine_tests(argv[1], argv[2], {test_remove_commit, test_remove_abort, test_remove_destroy, test_remove_inserted, test_put_and_remove, test_use_after_commit, test_use_after_abort}); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/engines/000077500000000000000000000000001410000423300155715ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/blackhole/000077500000000000000000000000001410000423300175155ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/blackhole/blackhole_test.cc000066400000000000000000000133511410000423300230120ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2017-2021, Intel Corporation */ #include "unittest.hpp" #include "../engine_scenarios/iterator.hpp" using namespace pmem::kv; /** * Blackhole engine is specific, it mostly does nothing, * but we can use it to test C++ API. */ static void BlackholeSimpleTest() { /** * TEST: Basic test for blackhole methods. */ db kv; auto s = kv.open("blackhole"); ASSERT_STATUS(s, status::OK); std::string value; std::size_t cnt = 1; std::string result; auto key = "key1"; (void)value; (void)result; (void)key; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, 0); ASSERT_STATUS(kv.get(key, &value), status::NOT_FOUND); ASSERT_STATUS(kv.put(key, "value1"), status::OK); ASSERT_STATUS(kv.exists(key), status::NOT_FOUND); cnt = 1; ASSERT_STATUS(kv.count_all(cnt), status::OK); UT_ASSERTeq(cnt, 0); ASSERT_STATUS(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(std::string(k, kb)); c->append(std::string(v, vb)); return 0; }, &result), status::NOT_FOUND); UT_ASSERT(result.empty()); ASSERT_STATUS(kv.get(key, &value), status::NOT_FOUND); ASSERT_STATUS(kv.remove(key), status::OK); ASSERT_STATUS(kv.get(key, &value), status::NOT_FOUND); ASSERT_STATUS(kv.get(key, nullptr, nullptr), status::NOT_FOUND); ASSERT_STATUS(kv.defrag(), status::NOT_SUPPORTED); kv.close(); } static void BlackholeRangeTest() { /** * TEST: Testf for all range methods (designed for sorted engines). */ db kv; auto s = kv.open("blackhole"); ASSERT_STATUS(s, status::OK); std::string result; std::size_t cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.put("key1", "value1"), status::OK); ASSERT_STATUS(kv.put("key2", "value2"), status::OK); ASSERT_STATUS(kv.put("key3", "value3"), status::OK); ASSERT_STATUS(kv.count_above("key1", cnt), status::OK); UT_ASSERTeq(0, cnt); ASSERT_STATUS(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); UT_ASSERT(result.empty()); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_equal_above("key1", cnt), status::OK); UT_ASSERTeq(0, cnt); ASSERT_STATUS(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); UT_ASSERT(result.empty()); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_below("key1", cnt), status::OK); UT_ASSERTeq(0, cnt); ASSERT_STATUS(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); UT_ASSERT(result.empty()); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_equal_below("key1", cnt), status::OK); UT_ASSERTeq(0, cnt); ASSERT_STATUS(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); UT_ASSERT(result.empty()); cnt = std::numeric_limits::max(); ASSERT_STATUS(kv.count_between("", "key3", cnt), status::OK); UT_ASSERTeq(0, cnt); ASSERT_STATUS(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); UT_ASSERT(result.empty()); kv.close(); } template typename std::enable_if::type test_write(iterator &it) { /* It does nothing for a read iterator */ } template typename std::enable_if::type test_write(iterator &it) { auto write_res = it.write_range(); UT_ASSERT(!write_res.is_ok()); ASSERT_STATUS(write_res.get_status(), status::NOT_SUPPORTED); ASSERT_STATUS(it.commit(), status::NOT_SUPPORTED); /* It returns void */ it.abort(); } template static void BlackholeIteratorTest() { db kv; auto s = kv.open("blackhole"); ASSERT_STATUS(s, status::OK); auto it = new_iterator(kv); ASSERT_STATUS(it.seek("abc"), status::OK); ASSERT_STATUS(it.seek_lower("abc"), status::NOT_SUPPORTED); ASSERT_STATUS(it.seek_lower_eq("abc"), status::NOT_SUPPORTED); ASSERT_STATUS(it.seek_higher("abc"), status::NOT_SUPPORTED); ASSERT_STATUS(it.seek_higher_eq("abc"), status::NOT_SUPPORTED); ASSERT_STATUS(it.seek_to_first(), status::NOT_SUPPORTED); ASSERT_STATUS(it.seek_to_last(), status::NOT_SUPPORTED); ASSERT_STATUS(it.is_next(), status::NOT_SUPPORTED); ASSERT_STATUS(it.next(), status::NOT_SUPPORTED); ASSERT_STATUS(it.prev(), status::NOT_SUPPORTED); test_write(it); auto key_res = it.key(); UT_ASSERT(!key_res.is_ok()); ASSERT_STATUS(key_res.get_status(), status::NOT_FOUND); auto read_res = it.read_range(); UT_ASSERT(!read_res.is_ok()); ASSERT_STATUS(read_res.get_status(), status::NOT_FOUND); kv.close(); } int main(int argc, char *argv[]) { return run_test([&] { BlackholeSimpleTest(); BlackholeRangeTest(); BlackholeIteratorTest(); BlackholeIteratorTest(); }); } pmemkv-1.5.0/tests/engines/blackhole/default.cmake000066400000000000000000000003241410000423300221420ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() make_config({}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/dram/000077500000000000000000000000001410000423300165145ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/dram/default.cmake000066400000000000000000000003241410000423300211410ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() make_config({}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/memkind_based/000077500000000000000000000000001410000423300203535ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/memkind_based/default.cmake000066400000000000000000000003651410000423300230050ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() make_config({"path":"${DIR}","size":${DB_SIZE}}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/memkind_based/default_no_config.cmake000066400000000000000000000003451410000423300250240ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() set(PATH "${DIR}") execute(${TEST_EXECUTABLE} ${ENGINE} ${PATH} ${DB_SIZE} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/memkind_based/memkind/000077500000000000000000000000001410000423300217775ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/memkind_based/memkind/error_handling.cmake000066400000000000000000000003271410000423300260000ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() execute(${TEST_EXECUTABLE} ${ENGINE} ${DIR}/nope/nope ${DIR} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/000077500000000000000000000000001410000423300203605ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/pmemobj_based/default.cmake000066400000000000000000000007431410000423300230120ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/default_no_config.cmake000066400000000000000000000007541410000423300250350ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() set(TEST_PATH "${DIR}/testfile") pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${TEST_PATH}) execute(${TEST_EXECUTABLE} ${ENGINE} ${TEST_PATH} ${DB_SIZE} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/helpers.cmake000066400000000000000000000004341410000423300230250ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation # # helpers.cmake - helper functions for tests' (cmake) scripts (pmemobj_based engines only) # set(LAYOUT "pmemkv") if (NOT ${ENGINE} STREQUAL "cmap") string(CONCAT LAYOUT "pmemkv_" ${ENGINE}) endif() pmemkv-1.5.0/tests/engines/pmemobj_based/persistent/000077500000000000000000000000001410000423300225605ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/pmemobj_based/persistent/insert_check.cmake000066400000000000000000000010521410000423300262210ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} insert ${PARAMS}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} check ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/000077500000000000000000000000001410000423300220115ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/create_if_missing.cmake000066400000000000000000000004351410000423300264670ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() make_config({"path":"${DIR}/testfile","create_if_missing":1,"size":83886080}) #80MB execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/error_handling_create.cmake000066400000000000000000000003211410000423300273270ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() execute(${TEST_EXECUTABLE} ${ENGINE} ${DIR}/testfile ${DIR}/nope/nope) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/error_handling_tx_oid.cmake000066400000000000000000000005011410000423300273520ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() pmempool_execute(create -l ${LAYOUT} -s 100M obj ${DIR}/testfile) execute(${TEST_EXECUTABLE} ${ENGINE} ${DIR}/testfile) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/error_handling_tx_path.cmake000066400000000000000000000006701410000423300275420ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() pmempool_execute(create -l ${LAYOUT} -s 100M obj ${DIR}/testfile) pmempool_execute(create -l ${LAYOUT} -s 100M obj ${DIR}/testfile2) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${DIR}/testfile2) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/put_get_std_map_create_or_error_if_exists.cmake000066400000000000000000000004461410000423300335060ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) setup() make_config({"path":"${DIR}/testfile","create_or_error_if_exists":1,"size":838860800}) #80MB execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmemobj/put_get_std_map_oid.cmake000066400000000000000000000005211410000423300270220ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) execute(${TEST_EXECUTABLE} ${ENGINE} ${DIR}/testfile ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/000077500000000000000000000000001410000423300223575ustar00rootroot00000000000000pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/erase.cmake000066400000000000000000000013611410000423300244610ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} create ${PARAMS}) pmreorder_create_store_log(${DIR}/testfile ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} erase ${PARAMS}) pmreorder_execute(true ReorderAccumulative ${PARENT_SRC_DIR}/engines/pmemobj_based/pmreorder/pmreorder.conf ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} open ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/insert.cmake000066400000000000000000000013621410000423300246670ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} create ${PARAMS}) pmreorder_create_store_log(${DIR}/testfile ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} insert ${PARAMS}) pmreorder_execute(true ReorderAccumulative ${PARENT_SRC_DIR}/engines/pmemobj_based/pmreorder/pmreorder.conf ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} open ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/iterator.cmake000066400000000000000000000013611410000423300252130ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} create ${PARAMS}) pmreorder_create_store_log(${DIR}/testfile ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} write ${PARAMS}) pmreorder_execute(true ReorderAccumulative ${PARENT_SRC_DIR}/engines/pmemobj_based/pmreorder/pmreorder.conf ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} open ${PARAMS}) finish() pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/pmreorder.conf000066400000000000000000000002371410000423300252270ustar00rootroot00000000000000{ "pmemobj_open" : "NoReorderNoCheck", "pmemobj_close" : "NoReorderNoCheck", "pmemobj_alloc" : "NoReorderNoCheck", "pmemobj_xalloc" : "NoReorderNoCheck" } pmemkv-1.5.0/tests/engines/pmemobj_based/pmreorder/recover.cmake000066400000000000000000000014111410000423300250230ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2021, Intel Corporation include(${PARENT_SRC_DIR}/helpers.cmake) include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) setup() if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) check_is_pmem(${DIR}/testfile) endif() if (${ENGINE} STREQUAL "robinhood") set(ENV{PMEMKV_ROBINHOOD_SHARDS_NUMBER} 64) endif() pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj ${DIR}/testfile) make_config({"path":"${DIR}/testfile"}) pmreorder_create_store_log(${DIR}/testfile ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} create ${PARAMS}) pmreorder_execute(true ReorderPartial ${PARENT_SRC_DIR}/engines/pmemobj_based/pmreorder/pmreorder.conf ${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} open ${PARAMS}) finish() pmemkv-1.5.0/tests/error_msg_test.cc000066400000000000000000000014431410000423300175100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ #include #include "unittest.hpp" static void errormsg_cleared() { pmem::kv::db kv; auto s = kv.open("non-existing name"); ASSERT_STATUS(s, pmem::kv::status::WRONG_ENGINE_NAME); auto err = pmem::kv::errormsg(); UT_ASSERT(err.size() > 0); s = kv.open("blackhole"); ASSERT_STATUS(s, pmem::kv::status::OK); std::string value; s = kv.get("Nonexisting key:", &value); ASSERT_STATUS(s, pmem::kv::status::NOT_FOUND); err = pmem::kv::errormsg(); UT_ASSERT(err == ""); UT_ASSERTeq(err.size(), 0); s = kv.open("non-existing name"); ASSERT_STATUS(s, pmem::kv::status::WRONG_ENGINE_NAME); err = pmem::kv::errormsg(); UT_ASSERT(err.size() > 0); } int main() { errormsg_cleared(); return 0; } pmemkv-1.5.0/tests/helpers.cmake000066400000000000000000000266171410000423300166210ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2021, Intel Corporation # # helpers.cmake - helper functions for cmake scripts used in tests. # # Sets testing path, based on TEST_DIR (as 'PARENT_DIR' here) passed to the top-level CMake. set(DIR ${PARENT_DIR}/${TEST_NAME}) string(REPLACE "|PARAM|" ";" PARAMS "${RAW_PARAMS}") # ----------------------------------------------------------------- # ## Define functions to control tests' flow and prepare environment # ----------------------------------------------------------------- # # setup the env, by removing old binary and testfile function(setup) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${PARENT_DIR}/${TEST_NAME}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PARENT_DIR}/${TEST_NAME}) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${BIN_DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BIN_DIR}) endfunction() function(print_logs) message(STATUS "Test ${TEST_NAME}:") if(EXISTS ${BIN_DIR}/${TEST_NAME}.out) file(READ ${BIN_DIR}/${TEST_NAME}.out OUT) message(STATUS "Stdout:\n${OUT}") endif() if(EXISTS ${BIN_DIR}/${TEST_NAME}.err) file(READ ${BIN_DIR}/${TEST_NAME}.err ERR) message(STATUS "Stderr:\n${ERR}") endif() if(EXISTS ${BIN_DIR}/${TEST_NAME}.pmreorder) file(READ ${BIN_DIR}/${TEST_NAME}.pmreorder PMEMREORDER) message(STATUS "Pmreorder:\n${PMEMREORDER}") endif() endfunction() # Performs cleanup and log matching. function(finish) print_logs() execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${PARENT_DIR}/${TEST_NAME}) endfunction() # Verifies ${log_file} matches ${match_file} using "match". function(match log_file match_file) execute_process(COMMAND ${PERL_EXECUTABLE} ${MATCH_SCRIPT} -o ${log_file} ${match_file} RESULT_VARIABLE MATCH_ERROR) if(MATCH_ERROR) message(FATAL_ERROR "Log does not match: ${MATCH_ERROR}") endif() endfunction() # Verifies file exists function(check_file_exists file) if(NOT EXISTS ${file}) message(FATAL_ERROR "${file} doesn't exist") endif() endfunction() # Verifies file doesn't exist function(check_file_doesnt_exist file) if(EXISTS ${file}) message(FATAL_ERROR "${file} exists") endif() endfunction() # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=810295 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=780173 # https://bugs.kde.org/show_bug.cgi?id=303877 # # valgrind issues an unsuppressable warning when exceeding # the brk segment, causing matching failures. We can safely # ignore it because malloc() will fallback to mmap() anyway. # # list of ignored warnings should match the list provided by PMDK: # https://github.com/pmem/pmdk/blob/master/src/test/unittest/unittest.sh function(valgrind_ignore_warnings valgrind_log) execute_process(COMMAND bash "-c" "cat ${valgrind_log} | grep -v \ -e \"WARNING: Serious error when reading debug info\" \ -e \"When reading debug info from \" \ -e \"Ignoring non-Dwarf2/3/4 block in .debug_info\" \ -e \"Last block truncated in .debug_info; ignoring\" \ -e \"parse_CU_Header: is neither DWARF2 nor DWARF3 nor DWARF4\" \ -e \"brk segment overflow\" \ -e \"see section Limitations in user manual\" \ -e \"Warning: set address range perms: large range\"\ -e \"further instances of this message will not be shown\"\ > ${valgrind_log}.tmp mv ${valgrind_log}.tmp ${valgrind_log}") endfunction() function(execute_common expect_success output_file name) if(TESTS_USE_FORCED_PMEM) set(ENV{PMEM_IS_PMEM_FORCE} 1) endif() if(${TRACER} STREQUAL pmemcheck) if(TESTS_USE_FORCED_PMEM) # pmemcheck runs really slow with pmem, disable it set(ENV{PMEM_IS_PMEM_FORCE} 0) endif() set(TRACE valgrind --error-exitcode=99 --tool=pmemcheck) elseif(${TRACER} STREQUAL memcheck) set(TRACE valgrind --error-exitcode=99 --tool=memcheck --leak-check=full --max-threads=3000 --suppressions=${TEST_ROOT_DIR}/ld.supp --suppressions=${TEST_ROOT_DIR}/memcheck-stdcpp.supp --suppressions=${TEST_ROOT_DIR}/memcheck-libunwind.supp --suppressions=${TEST_ROOT_DIR}/memcheck-ndctl.supp) elseif(${TRACER} STREQUAL helgrind) set(TRACE valgrind --error-exitcode=99 --tool=helgrind --max-threads=3000) elseif(${TRACER} STREQUAL drd) set(TRACE valgrind --error-exitcode=99 --tool=drd --max-threads=3000 --suppressions=${TEST_ROOT_DIR}/drd.supp) elseif(${TRACER} STREQUAL gdb) set(TRACE gdb --batch --command=${GDB_BATCH_FILE} --args) elseif(${TRACER} MATCHES "none.*") # nothing else() message(FATAL_ERROR "Unknown tracer '${TRACER}'") endif() if (NOT $ENV{CGDB}) if (NOT WIN32) set(TRACE timeout -s SIGALRM -k 200s 180s ${TRACE}) endif() endif() string(REPLACE ";" " " TRACE_STR "${TRACE}") message(STATUS "Executing: ${TRACE_STR} ${name} ${ARGN}") set(cmd ${TRACE} ${name} ${ARGN}) if($ENV{CGDB}) find_program(KONSOLE NAMES konsole) find_program(GNOME_TERMINAL NAMES gnome-terminal) find_program(CGDB NAMES cgdb) if (NOT KONSOLE AND NOT GNOME_TERMINAL) message(FATAL_ERROR "konsole or gnome-terminal not found.") elseif (NOT CGDB) message(FATAL_ERROR "cdgb not found.") elseif(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Cannot use cgdb with ${TRACER}") else() if (KONSOLE) set(cmd konsole -e cgdb --args ${cmd}) elseif(GNOME_TERMINAL) set(cmd gnome-terminal --tab --active --wait -- cgdb --args ${cmd}) endif() endif() endif() if(${output_file} STREQUAL none) execute_process(COMMAND ${cmd} OUTPUT_QUIET RESULT_VARIABLE res) else() execute_process(COMMAND ${cmd} RESULT_VARIABLE res OUTPUT_FILE ${BIN_DIR}/${TEST_NAME}.out ERROR_FILE ${BIN_DIR}/${TEST_NAME}.err) endif() print_logs() # pmemcheck is a special snowflake and it doesn't set exit code when # it detects an error, so we have to look at its output if match file # was not found. if(${TRACER} STREQUAL pmemcheck) if(NOT EXISTS ${BIN_DIR}/${TEST_NAME}.err) message(FATAL_ERROR "${TEST_NAME}.err not found.") endif() file(READ ${BIN_DIR}/${TEST_NAME}.err PMEMCHECK_ERR) message(STATUS "Stderr:\n${PMEMCHECK_ERR}\nEnd of stderr") if(NOT PMEMCHECK_ERR MATCHES "ERROR SUMMARY: 0") message(FATAL_ERROR "${TRACE} ${name} ${ARGN} failed: ${res}") endif() endif() if(res AND expect_success) message(FATAL_ERROR "${TRACE} ${name} ${ARGN} failed: ${res}") endif() if(NOT res AND NOT expect_success) message(FATAL_ERROR "${TRACE} ${name} ${ARGN} unexpectedly succeeded: ${res}") endif() if(TESTS_USE_FORCED_PMEM) unset(ENV{PMEM_IS_PMEM_FORCE}) endif() endfunction() # Checks if target (test binary) was built function(check_target name) if(NOT EXISTS ${name}) message(FATAL_ERROR "Test file: \"${name}\" was not found! If test wasn't built, run make first.") endif() endfunction() # Generic command executor which handles failures and prints command output # to specified file. function(execute_with_output out name) check_target(${name}) execute_common(true ${out} ${name} ${ARGN}) endfunction() # Generic command executor which handles failures but ignores output. function(execute_ignore_output name) check_target(${name}) execute_common(true none ${name} ${ARGN}) endfunction() # Executes test command ${name} and verifies its status. # First argument of the command is test directory name. # Optional function arguments are passed as consecutive arguments to # the command. function(execute name) check_target(${name}) execute_common(true ${TRACER}_${TESTCASE} ${name} ${ARGN}) endfunction() # Trim '{' and '}' characters from the beginning and end function(trim_config config_string out_config) string(LENGTH ${config_string} config_len) math(EXPR config_len "${config_len}-2") string(SUBSTRING ${config_string} 1 ${config_len} out) set(${out_config} ${out} PARENT_SCOPE) endfunction() # Expects config in form of a json-like list, e.g. # make_config({"path":"/path/to/file"}) function(make_config) string(REPLACE " " "" config ${ARGN}) if ("${EXTRA_CONFIG_PARAMS}" STREQUAL "") set(CONFIG "${config}" CACHE INTERNAL "") else() trim_config(${config} trimmed_config) trim_config(${EXTRA_CONFIG_PARAMS} extra_config_params) set(CONFIG "{${trimmed_config},${extra_config_params}}" CACHE INTERNAL "") endif() endfunction() # Executes command ${name} and creates a storelog. # First argument is pool file. # Second argument is test executable. # Optional function arguments are passed as consecutive arguments to # the command. function(pmreorder_create_store_log pool name) check_target(${name}) if(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Pmreorder test must be run without any tracer.") endif() configure_file(${pool} ${pool}.copy COPYONLY) 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(cmd valgrind --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 ${name} ${ARGN}) execute_common(true ${TRACER}_${TESTCASE} ${cmd}) file(READ ${BIN_DIR}/${TEST_NAME}.storelog STORELOG) string(REPLACE "operator|=" "operator_OR" FIXED_STORELOG "${STORELOG}") file(WRITE ${BIN_DIR}/${TEST_NAME}.storelog "${FIXED_STORELOG}") unset(ENV{PMREORDER_EMIT_LOG}) file(REMOVE ${pool}) configure_file(${pool}.copy ${pool} COPYONLY) endfunction() # Executes pmreorder. # First argument is expected result. # Second argument is engine type. # Third argument is path to configure file. # Fourth argument is path to the checker program. # Optional function arguments are passed as consecutive arguments to # the command. function(pmreorder_execute expect_success engine conf_file name) check_target(${name}) if(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Pmreorder test must be run without any tracer.") endif() set(ENV{PMEMOBJ_CONF} "copy_on_write.at_open=1") string(REPLACE "\"" "\\\"" ESCAPED_ARGN "${ARGN}") set(cmd pmreorder -l ${BIN_DIR}/${TEST_NAME}.storelog -o ${BIN_DIR}/${TEST_NAME}.pmreorder -r ${engine} -p "${name} ${ESCAPED_ARGN}" -x ${conf_file}) execute_common(${expect_success} ${TRACER}_${TESTCASE} ${cmd}) unset(ENV{PMEMOBJ_CONF}) endfunction() function(pmempool_execute) set(ENV{LD_LIBRARY_PATH} ${LIBPMEMOBJ++_LIBRARY_DIRS}) execute_process(COMMAND pmempool ${ARGN}) unset(ENV{LD_LIBRARY_PATH}) endfunction() # Executes test command ${name} under GDB. # First argument of the command is a gdb batch file. # Second argument of the command is the test command. # Optional function arguments are passed as consecutive arguments to # the command. function(crash_with_gdb gdb_batch_file name) check_target(${name}) set(PREV_TRACER ${TRACER}) set(TRACER gdb) set(GDB_BATCH_FILE ${gdb_batch_file}) execute_common(true ${TRACER}_${TESTCASE} ${name} ${ARGN}) set(TRACER ${PREV_TRACER}) endfunction() # Checks whether specified filename is located on persistent memory and emits # FATAL_ERROR in case it's not. function(check_is_pmem filename) execute_process(COMMAND ${BIN_DIR}/../check_is_pmem ${filename} RESULT_VARIABLE is_pmem) if (${is_pmem} EQUAL 2) message(FATAL_ERROR "check_is_pmem failed.") elseif ((${is_pmem} EQUAL 1) AND (NOT TESTS_USE_FORCED_PMEM)) # Return value 1 means that path points to non-pmem message(FATAL_ERROR "${TEST_NAME} can only be run on PMEM.") endif() endfunction() pmemkv-1.5.0/tests/ld.supp000066400000000000000000000013471410000423300154560ustar00rootroot00000000000000{ Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/x86_64-linux-gnu/ld-2.*.so obj:* obj:* } { Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:handle_ld_preload fun:dl_main fun:_dl_sysdep_start fun:_dl_start obj:/lib/x86_64-linux-gnu/ld-2.*.so obj:* } { Memcheck:Leak ... fun:_dl_init fun:dl_open_worker fun:_dl_catch_error ... } pmemkv-1.5.0/tests/match000077500000000000000000000163111410000423300151650ustar00rootroot00000000000000#!/usr/bin/env perl # SPDX-License-Identifier: BSD-3-Clause # Copyright 2014-2020, Intel Corporation # # # match -- compare an output file with expected results # # usage: match [-adoqv] [match-file]... # # this script compares the output from a test run, stored in a file, with # the expected output. comparison is done line-by-line until either all # lines compare correctly (exit code 0) or a miscompare is found (exit # code nonzero). # # expected output is stored in a ".match" file, which contains a copy of # the expected output with embedded tokens for things that should not be # exact matches. the supported tokens are: # # $(N) an integer (i.e. one or more decimal digits) # $(NC) one or more decimal digits with comma separators # $(FP) a floating point number # $(S) ascii string # $(X) hex number # $(XX) hex number prefixed with 0x # $(W) whitespace # $(nW) non-whitespace # $(*) any string # $(DD) output of a "dd" run # $(OPT) line is optional (may be missing, matched if found) # $(OPX) ends a contiguous list of $(OPT)...$(OPX) lines, at least # one of which must match # ${string1|string2} string1 OR string2 # # Additionally, if any "X.ignore" file exists, strings or phrases found per # line in the file will be ignored if found as a substring in the # corresponding output file (making it easy to skip entire output lines). # # arguments are: # # -a find all files of the form "X.match" in the current # directory and match them again the corresponding file "X". # # -o custom output filename - only one match file can be given # # -d debug -- show lots of debug output # # -q don't print log files on mismatch # # -v verbose -- show every line as it is being matched # use strict; use Getopt::Std; use Encode; use v5.16; select STDERR; binmode(STDOUT, ":utf8"); binmode(STDERR, ":utf8"); my $Me = $0; $Me =~ s,.*/,,; our ($opt_a, $opt_d, $opt_q, $opt_v, $opt_o); $SIG{HUP} = $SIG{INT} = $SIG{TERM} = $SIG{__DIE__} = sub { die @_ if $^S; my $errstr = shift; die "FAIL: $Me: $errstr"; }; sub usage { my $msg = shift; warn "$Me: $msg\n" if $msg; warn "Usage: $Me [-adqv] [match-file]...\n"; warn " or: $Me [-dqv] -o output-file match-file...\n"; exit 1; } getopts('adoqv') or usage; my %match2file; if ($opt_a) { usage("-a and filename arguments are mutually exclusive") if $#ARGV != -1; opendir(DIR, '.') or die "opendir: .: $!\n"; my @matchfiles = grep { /(.*)\.match$/ && -f $1 } readdir(DIR); closedir(DIR); die "no files found to process\n" unless @matchfiles; foreach my $mfile (@matchfiles) { die "$mfile: $!\n" unless open(F, $mfile); close(F); my $ofile = $mfile; $ofile =~ s/\.match$//; die "$mfile found but cannot open $ofile: $!\n" unless open(F, $ofile); close(F); $match2file{$mfile} = $ofile; } } elsif ($opt_o) { usage("-o argument requires two paths") if $#ARGV != 1; $match2file{$ARGV[1]} = $ARGV[0]; } else { usage("no match-file arguments found") if $#ARGV == -1; # to improve the failure case, check all filename args exist and # are provided in pairs now, before going through and processing them foreach my $mfile (@ARGV) { my $ofile = $mfile; usage("$mfile: not a .match file") unless $ofile =~ s/\.match$//; usage("$mfile: $!") unless open(F, $mfile); close(F); usage("$ofile: $!") unless open(F, $ofile); close(F); $match2file{$mfile} = $ofile; } } my $mfile; my $ofile; my $ifile; print "Files to be processed:\n" if $opt_v; foreach $mfile (sort keys %match2file) { $ofile = $match2file{$mfile}; $ifile = $ofile . ".ignore"; $ifile = undef unless (-f $ifile); if ($opt_v) { print " match-file \"$mfile\" output-file \"$ofile\""; if ($ifile) { print " ignore-file $ifile\n"; } else { print "\n"; } } match($mfile, $ofile, $ifile); } exit 0; # # strip_it - user can optionally ignore lines from files that contain # any number of substrings listed in a file called "X.ignore" where X # is the name of the output file. # sub strip_it { my ($ifile, $file, $input) = @_; # if there is no ignore file just return unaltered input return $input unless $ifile; my @lines_in = split /^/, $input; my $output; my $line_in; my @i_file = split /^/, snarf($ifile); my $i_line; my $ignore_it = 0; foreach $line_in (@lines_in) { my @i_lines = @i_file; foreach $i_line (@i_lines) { chop($i_line); if (index($line_in, $i_line) != -1) { $ignore_it = 1; if ($opt_v) { print "Ignoring (from $file): $line_in"; } } } if ($ignore_it == 0) { $output .= $line_in; } $ignore_it = 0; } return $output; } # # match -- process a match-file, output-file pair # sub match { my ($mfile, $ofile, $ifile) = @_; my $pat; my $output = snarf($ofile); $output = strip_it($ifile, $ofile, $output); my $all_lines = $output; my $line_pat = 0; my $line_out = 0; my $opt = 0; my $opx = 0; my $opt_found = 0; my $fstr = snarf($mfile); $fstr = strip_it($ifile, $mfile, $fstr); for (split /^/, $fstr) { $pat = $_; $line_pat++; $line_out++; s/([*+?|{}.\\^\$\[()])/\\$1/g; s/\\\$\\\(FP\\\)/[-+]?\\d*\\.?\\d+([eE][-+]?\\d+)?/g; s/\\\$\\\(N\\\)/[-+]?\\d+/g; s/\\\$\\\(NC\\\)/[-+]?\\d+(,[0-9]+)*/g; s/\\\$\\\(\\\*\\\)/\\p{Print}*/g; s/\\\$\\\(S\\\)/\\P{IsC}+/g; s/\\\$\\\(X\\\)/\\p{XPosixXDigit}+/g; s/\\\$\\\(XX\\\)/0x\\p{XPosixXDigit}+/g; s/\\\$\\\(W\\\)/\\p{Blank}*/g; s/\\\$\\\(nW\\\)/\\p{Graph}*/g; s/\\\$\\\{([^|]*)\\\|([^|]*)\\\}/($1|$2)/g; s/\\\$\\\(DD\\\)/\\d+\\+\\d+ records in\n\\d+\\+\\d+ records out\n\\d+ bytes \\\(\\d+ .B\\\) copied, [.0-9e-]+[^,]*, [.0-9]+ .B.s/g; if (s/\\\$\\\(OPT\\\)//) { $opt = 1; } elsif (s/\\\$\\\(OPX\\\)//) { $opx = 1; } else { $opt_found = 0; } if ($opt_v) { my @lines = split /\n/, $output; my $line; if (@lines) { $line = $lines[0]; } else { $line = "[EOF]"; } printf("%s:%-3d %s%s:%-3d %s\n", $mfile, $line_pat, $pat, $ofile, $line_out, $line); } print " => /$_/\n" if $opt_d; print " [$output]\n" if $opt_d; unless ($output =~ s/^$_//) { if ($opt || ($opx && $opt_found)) { printf("%s:%-3d [skipping optional line]\n", $ofile, $line_out) if $opt_v; $line_out--; $opt = 0; } else { if (!$opt_v) { if ($opt_q) { print "[MATCHING FAILED]\n"; } else { print "[MATCHING FAILED, COMPLETE FILE ($ofile) BELOW]\n$all_lines\n[EOF]\n"; } $opt_v = 1; match($mfile, $ofile); } die "$mfile:$line_pat did not match pattern\n"; } } elsif ($opt) { $opt_found = 1; } $opx = 0; } if ($output ne '') { if (!$opt_v) { if ($opt_q) { print "[MATCHING FAILED]\n"; } else { print "[MATCHING FAILED, COMPLETE FILE ($ofile) BELOW]\n$all_lines\n[EOF]\n"; } } # make it a little more print-friendly... $output =~ s/\n/\\n/g; die "line $line_pat: unexpected output: \"$output\"\n"; } } # # snarf -- slurp an entire file into memory # sub snarf { my ($file) = @_; my $fh; open($fh, '<', $file) or die "$file $!\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"; } pmemkv-1.5.0/tests/memcheck-libunwind.supp000066400000000000000000000010141410000423300206130ustar00rootroot00000000000000{ generic libunwind suppression Memcheck:Param msync(start) ... obj:*libunwind* ... } { generic libunwind suppression Memcheck:Param rt_sigprocmask(set) ... obj:*libunwind* ... } { generic libunwind suppression Memcheck:Addr8 ... obj:*libunwind* ... } { libunwind exception suppresion Memcheck:Param write(buf) ... obj:*libunwind* ... } { libunwind calls glibc's setcontext on some architectures, e.g. ppc Memcheck:Addr8 fun:setcontext* ... } pmemkv-1.5.0/tests/memcheck-ndctl.supp000066400000000000000000000010671410000423300177340ustar00rootroot00000000000000{ 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 ... } { memory leak in ndctl 63 Memcheck:Leak match-leak-kinds: reachable fun:malloc ... fun:ndctl_namespace_get_first_badblock ... } { cond jump in libkmod Memcheck:Cond ... fun:kmod_module_new_from_lookup ... fun:ndctl_region_get_first ... } pmemkv-1.5.0/tests/memcheck-stdcpp.supp000066400000000000000000000004611410000423300201220ustar00rootroot00000000000000{ https://bugs.kde.org/show_bug.cgi?id=345307, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65434, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64535 Memcheck:Leak match-leak-kinds: reachable fun:malloc obj:*/libstdc++.so.* fun:call_init.part.0 ... fun:_dl_init obj:*/ld-*.so } pmemkv-1.5.0/tests/result/000077500000000000000000000000001410000423300154575ustar00rootroot00000000000000pmemkv-1.5.0/tests/result/result.cpp000066400000000000000000000213061410000423300175030ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2020, Intel Corporation */ /* * result.cpp -- tests result class, its members and related exception. */ #include #include "../common/unittest.hpp" #include "libpmemkv.hpp" using slice = pmem::obj::slice; template using result = pmem::kv::result; /* number of possible statuses */ const size_t number_of_statuses = 13; class moveable { public: moveable(int val) : move(false), x(val) { } moveable(const moveable &other) = delete; moveable(moveable &&other) { x = other.x; other.x = -1; move = false; other.move = true; } moveable &operator=(moveable &&other) { x = other.x; other.x = -1; other.move = true; return *this; } bool moved() { return move && x == -1; } int get() { return x; } private: bool move; int x; }; static void basic_test() { const std::string str = "abcdefgh"; /* result with correct value */ result res1(slice(&str[0], &str[str.size()])); UT_ASSERT(res1.is_ok()); ASSERT_STATUS(res1.get_status(), pmem::kv::status::OK); UT_ASSERT(res1 == pmem::kv::status::OK); UT_ASSERT(pmem::kv::status::OK == res1); UT_ASSERT(res1 != pmem::kv::status::NOT_FOUND); UT_ASSERT(pmem::kv::status::NOT_FOUND != res1); UT_ASSERTeq(str.compare(res1.get_value().begin()), 0); /* test const get_value() */ const auto const_res(res1); UT_ASSERT(const_res.is_ok()); UT_ASSERTeq(str.compare(const_res.get_value().begin()), 0); /* result without value */ for (size_t i = 1; i < number_of_statuses; ++i) { result res2(static_cast(i)); UT_ASSERT(!res2.is_ok()); UT_ASSERTeq(res2.get_status(), static_cast(i)); UT_ASSERT(res2 == static_cast(i)); UT_ASSERT(static_cast(i) == res2); UT_ASSERT(res2 != pmem::kv::status::OK); UT_ASSERT(pmem::kv::status::OK != res2); bool exception_thrown = false; try { res2.get_value(); } catch (pmem::kv::bad_result_access &e) { UT_ASSERTeq(std::string(e.what()).compare( "bad_result_access: value doesn't exist"), 0); exception_thrown = true; } UT_ASSERT(exception_thrown); } /* const result without value */ for (size_t i = 1; i < number_of_statuses; ++i) { const result const_res(static_cast(i)); UT_ASSERT(!const_res.is_ok()); UT_ASSERTeq(const_res.get_status(), static_cast(i)); UT_ASSERT(const_res == static_cast(i)); UT_ASSERT(static_cast(i) == const_res); UT_ASSERT(const_res != pmem::kv::status::OK); UT_ASSERT(pmem::kv::status::OK != const_res); bool exception_thrown = false; try { const_res.get_value(); } catch (pmem::kv::bad_result_access &e) { UT_ASSERTeq(std::string(e.what()).compare( "bad_result_access: value doesn't exist"), 0); exception_thrown = true; } UT_ASSERT(exception_thrown); } /* test copy ctor */ result result1(res1); UT_ASSERT(result1.is_ok()); ASSERT_STATUS(result1.get_status(), pmem::kv::status::OK); UT_ASSERTeq(str.compare(result1.get_value().begin()), 0); /* test copy assignment */ result result2(pmem::kv::status::NOT_FOUND); result2 = res1; UT_ASSERT(result2.is_ok()); ASSERT_STATUS(result2.get_status(), pmem::kv::status::OK); UT_ASSERTeq(str.compare(result2.get_value().begin()), 0); /* test move ctor */ result to_move(moveable(10)); auto &ref = to_move.get_value(); result move_result(std::move(to_move)); UT_ASSERT(move_result.is_ok()); ASSERT_STATUS(move_result.get_status(), pmem::kv::status::OK); UT_ASSERTeq(move_result.get_value().get(), 10); ASSERT_STATUS(to_move.get_status(), pmem::kv::status::UNKNOWN_ERROR); UT_ASSERT(ref.moved()); /* test move assignment*/ result to_move2(moveable(10)); auto &ref2 = to_move2.get_value(); result move_result2(pmem::kv::status::NOT_FOUND); move_result2 = std::move(to_move2); UT_ASSERT(move_result2.is_ok()); ASSERT_STATUS(move_result2.get_status(), pmem::kv::status::OK); UT_ASSERTeq(move_result2.get_value().get(), 10); ASSERT_STATUS(to_move2.get_status(), pmem::kv::status::UNKNOWN_ERROR); UT_ASSERT(ref2.moved()); /* test ctor with rvalue reference to T */ moveable to_move3(10); result move_result3(std::move(to_move3)); UT_ASSERT(move_result3.is_ok()); ASSERT_STATUS(move_result3.get_status(), pmem::kv::status::OK); UT_ASSERTeq(move_result3.get_value().get(), 10); UT_ASSERT(to_move3.moved()); /* test result with trivial type */ result trivial1(10); UT_ASSERT(trivial1.is_ok()); UT_ASSERTeq(trivial1.get_value(), 10); result trivial2(pmem::kv::status::NOT_FOUND); UT_ASSERT(!trivial2.is_ok()); trivial2 = trivial1; UT_ASSERT(trivial2.is_ok()); UT_ASSERTeq(trivial2.get_value(), 10); trivial1 = trivial2; UT_ASSERT(trivial1.is_ok()); UT_ASSERTeq(trivial1.get_value(), 10); trivial2 = std::move(trivial1); UT_ASSERT(!trivial1.is_ok()); UT_ASSERT(trivial2.is_ok()); trivial1 = std::move(trivial2); UT_ASSERT(trivial1.is_ok()); UT_ASSERT(!trivial2.is_ok()); /* test moving value out of the result */ result move_out1(moveable(10)); UT_ASSERT(move_out1.is_ok()); auto &val = move_out1.get_value(); auto moved_val1 = std::move(move_out1).get_value(); UT_ASSERT(!move_out1.is_ok()); UT_ASSERT(val.moved()); UT_ASSERTeq(moved_val1.get(), 10); /* test moving value out of the empty result */ bool exception_thrown = false; try { auto moved_val2 = std::move(move_out1).get_value(); (void)moved_val2; } catch (const pmem::kv::bad_result_access &e) { exception_thrown = true; } UT_ASSERT(exception_thrown); } /* counter of constructor/destructor calls */ class cd_counter { public: static size_t des_cnt, copy_cnt, move_cnt; cd_counter() { } cd_counter(const cd_counter &other) { ++cd_counter::copy_cnt; } cd_counter(cd_counter &&other) { ++cd_counter::move_cnt; } ~cd_counter() { ++cd_counter::des_cnt; } cd_counter &operator=(const cd_counter &other) { ++cd_counter::copy_cnt; return *this; } cd_counter &operator=(cd_counter &&other) { ++cd_counter::move_cnt; return *this; } }; size_t cd_counter::des_cnt; size_t cd_counter::copy_cnt; size_t cd_counter::move_cnt; /* test if T's constructors/destructors are properly called */ static void constructor_destructor_test() { cd_counter c; { result r(c); UT_ASSERTeq(cd_counter::copy_cnt, 1); } /* test if value inside of the result was destroyed */ UT_ASSERTeq(cd_counter::des_cnt, 1); { result r(c); UT_ASSERTeq(cd_counter::copy_cnt, 2); result to_copy(pmem::kv::status::NOT_FOUND); /* if you copy an empty result to the ok result, value's destructor should * be called */ r = to_copy; UT_ASSERTeq(cd_counter::des_cnt, 2); } UT_ASSERTeq(cd_counter::des_cnt, 2); { /* check copy ctor/assignment */ result r1(c); result r2(c); UT_ASSERTeq(cd_counter::copy_cnt, 4); r1 = r2; UT_ASSERTeq(cd_counter::copy_cnt, 5); /* if there is only status, value's copy ctor shouldn't be called */ result r3(pmem::kv::status::NOT_FOUND); result r4(r3); UT_ASSERTeq(cd_counter::copy_cnt, 5); /* if there is only status, value's copy assignment operator shouldn't be * called */ r3 = r4; UT_ASSERTeq(cd_counter::copy_cnt, 5); /* check move ctor/assignment */ result r5(std::move(r1)); UT_ASSERTeq(cd_counter::move_cnt, 1); result r6(pmem::kv::status::NOT_FOUND); r6 = std::move(r2); UT_ASSERTeq(cd_counter::move_cnt, 2); /* if there is only status, value's move ctor shouldn't be called */ result r7(pmem::kv::status::NOT_FOUND); result r8(std::move(r7)); UT_ASSERTeq(cd_counter::move_cnt, 2); /* if there is only status, value's move assignment operator shouldn't be * called */ r7 = std::move(r8); UT_ASSERTeq(cd_counter::move_cnt, 2); /* move value to constructor */ result r9(std::move(c)); UT_ASSERTeq(cd_counter::move_cnt, 3); /* if there is a value in each result, move assignment operator should be * called */ result r10(c); r10 = std::move(r9); UT_ASSERTeq(cd_counter::move_cnt, 4); /* if you move an empty result to the ok result, value's destructor should * be called */ result r11(pmem::kv::status::NOT_FOUND); r10 = std::move(r11); /* previous value in r10 should be destroyed */ UT_ASSERTeq(cd_counter::des_cnt, 3); } /* check if values in r5, r6 were destroyed */ UT_ASSERTeq(cd_counter::des_cnt, 5); } static void test(int argc, char *argv[]) { basic_test(); constructor_destructor_test(); } int main(int argc, char *argv[]) { return run_test([&] { test(argc, argv); }); } pmemkv-1.5.0/tests/true.cmake000066400000000000000000000002111410000423300161140ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation # true.cmake - cmake script which always succeeds return() pmemkv-1.5.0/tests/wrong_engine_name_test.cc000066400000000000000000000036001410000423300211670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ #include #include "unittest.hpp" /** * Tests if open() returns WRONG_ENGINE_NAME if wrong engine's name is passed. */ static bool wrong_engine_name_test(std::string name) { /** * TEST: if engine is not switched on (in CMake) it should not open */ pmem::kv::db db; return db.open(name) == pmem::kv::status::WRONG_ENGINE_NAME; } static void errormsg_test() { /** * TEST: using WRONG_ENGINE_NAME status, we check if errormsg is properly set */ pmem::kv::db kv; auto s = kv.open("non-existing name"); ASSERT_STATUS(s, pmem::kv::status::WRONG_ENGINE_NAME); auto err = pmem::kv::errormsg(); UT_ASSERT(err.size() > 0); s = kv.open("non-existing name"); ASSERT_STATUS(s, pmem::kv::status::WRONG_ENGINE_NAME); s = kv.open("non-existing name"); ASSERT_STATUS(s, pmem::kv::status::WRONG_ENGINE_NAME); /* Test whether errormsg is cleared correctly after each error */ UT_ASSERT(pmem::kv::errormsg() == err); /* Test if instance of db reports the same error */ UT_ASSERT(kv.errormsg() == err); kv.close(); } int main() { UT_ASSERT(wrong_engine_name_test("non_existent_name")); #ifndef ENGINE_CMAP UT_ASSERT(wrong_engine_name_test("cmap")); #endif #ifndef ENGINE_VSMAP UT_ASSERT(wrong_engine_name_test("vsmap")); #endif #ifndef ENGINE_VCMAP UT_ASSERT(wrong_engine_name_test("vcmap")); #endif #ifndef ENGINE_CSMAP UT_ASSERT(wrong_engine_name_test("csmap")); #endif #ifndef ENGINE_TREE3 UT_ASSERT(wrong_engine_name_test("tree3")); #endif #ifndef ENGINE_STREE UT_ASSERT(wrong_engine_name_test("stree")); #endif #ifndef ENGINE_RADIX UT_ASSERT(wrong_engine_name_test("radix")); #endif #ifndef ENGINE_ROBINHOOD UT_ASSERT(wrong_engine_name_test("robinhood")); #endif #ifndef ENGINE_DRAM_VCMAP UT_ASSERT(wrong_engine_name_test("dram_vcmap")); #endif errormsg_test(); return 0; } pmemkv-1.5.0/utils/000077500000000000000000000000001410000423300141375ustar00rootroot00000000000000pmemkv-1.5.0/utils/check_license/000077500000000000000000000000001410000423300167165ustar00rootroot00000000000000pmemkv-1.5.0/utils/check_license/check-headers.sh000077500000000000000000000115271410000423300217510ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # 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)" echo " -d, --update_dates change Copyright dates in all analyzed files (rather not use with -a)" } if [ "$#" -lt 2 ]; then usage >&2 exit 2 fi SOURCE_ROOT=$1 shift LICENSE=$1 shift PATTERN=`mktemp` TMP=`mktemp` TMP2=`mktemp` TEMPFILE=`mktemp` rm -f $PATTERN $TMP $TMP2 if [ "$1" == "-h" -o "$1" == "--help" ]; then usage exit 0 fi 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 UPDATE_DATES=0 while [ "$1" != "" ]; do case $1 in -v|--verbose) VERBOSE=1 ;; -a|--all) CHECK_ALL=1 ;; -d|--update_dates) UPDATE_DATES=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 if [ $VERBOSE -eq 1 ]; then 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 $LICENSE -a)" echo fi 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 '*\.[chs]$' -e '*\.[ch]pp$' -e '*\.sh$' \ -e '*\.link$' -e 'Makefile*' -e 'TEST*' \ -e '/common.inc$' -e '/match$' -e '/check_whitespace$' \ -e 'LICENSE$' -e 'CMakeLists.txt$' -e '*\.cmake$' | \ xargs) 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 if ! grep -q "SPDX-License-Identifier: $LICENSE" $src_path; then echo >&2 "error: no $LICENSE SPDX tag in file: $src_path" RV=1 fi 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` YEARS=`sed ' /Copyright [0-9-]\+.*, Intel Corporation/!d s/.*Copyright \([0-9]\+\)-\([0-9]\+\),.*/\1-\2/ s/.*Copyright \([0-9]\+\),.*/\1-\1/' $src_path` if [ -z "$YEARS" ]; then echo >&2 "No copyright years in $src_path" RV=1 continue fi HEADER_FIRST=`echo $YEARS | cut -d"-" -f1` HEADER_LAST=` echo $YEARS | cut -d"-" -f2` 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 if [ ${UPDATE_DATES} -eq 1 ]; then sed -i "s/Copyright ${YEARS}/Copyright ${NEW}/g" "${src_path}" else echo "$file:1: error: wrong copyright date: (is: $YEARS, should be: $NEW)" >&2 RV=1 fi fi else echo "error: unknown commit dates in file: $file" >&2 RV=1 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.5.0/utils/check_license/file-exceptions.sh000077500000000000000000000003351410000423300223540ustar00rootroot00000000000000#!/bin/sh -e # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # file-exceptions.sh - filter out files not checked for copyright and license grep -v -E -e 'src/valgrind|src/fast_hash.*' pmemkv-1.5.0/utils/check_whitespace000077500000000000000000000061641410000423300173650ustar00rootroot00000000000000#!/usr/bin/env perl # SPDX-License-Identifier: BSD-3-Clause # Copyright 2015-2019, Intel Corporation # # 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.5.0/utils/cppstyle000077500000000000000000000015521410000423300157330ustar00rootroot00000000000000#!/usr/bin/perl -w # SPDX-License-Identifier: BSD-3-Clause # Copyright 2017, Intel Corporation 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.5.0/utils/docker/000077500000000000000000000000001410000423300154065ustar00rootroot00000000000000pmemkv-1.5.0/utils/docker/0001-travis-fix-travisci_build_coverity_scan.sh.patch000066400000000000000000000016251410000423300274470ustar00rootroot00000000000000From 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.5.0/utils/docker/build.sh000077500000000000000000000102661410000423300170510ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2017-2021, Intel Corporation # # build.sh - runs a Docker container from a Docker image with environment # prepared for running pmemkv builds and tests. It uses Docker image # tagged as described in ./images/build-image.sh. # # 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 at the list of Dockerfiles at the # utils/docker/images directory), eg. OS=ubuntu, OS_VER=19.10. # set -e source $(dirname ${0})/set-ci-vars.sh IMG_VER=${IMG_VER:-devel} TAG="${OS}-${OS_VER}-${IMG_VER}" if [[ -z "${OS}" || -z "${OS_VER}" ]]; then echo "ERROR: The variables OS and OS_VER have to be set " \ "(eg. OS=fedora, OS_VER=31)." 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 imageName=${CONTAINER_REG}:${TAG} containerName=pmemkv-${OS}-${OS_VER} if [[ "${command}" == "" ]]; then case ${TYPE} in debug) builds=(tests_gcc_debug_cpp11 tests_gcc_debug_cpp14) command="./run-build.sh ${builds[@]}"; ;; release) builds=(tests_clang_release_cpp20 test_release_installation) command="./run-build.sh ${builds[@]}"; ;; valgrind) builds=(tests_gcc_debug_cpp14_valgrind_other) command="./run-build.sh ${builds[@]}"; ;; memcheck_drd) builds=(tests_gcc_debug_cpp14_valgrind_memcheck_drd) command="./run-build.sh ${builds[@]}"; ;; compatibility) command="./run-compatibility.sh"; ;; building) command="./run-test-building.sh"; ;; coverity) command="./run-coverity.sh"; ;; bindings) command="./run-bindings.sh"; ;; doc) command="./run-doc-update.sh"; ;; *) echo "ERROR: wrong build TYPE" exit 1 ;; esac fi if [ "${COVERAGE}" == "1" ]; then docker_opts="${docker_opts} `bash <(curl -s https://codecov.io/env)`"; fi if [ -n "${DNS_SERVER}" ]; then DNS_SETTING=" --dns=${DNS_SERVER} "; fi # 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='' WORKDIR=/pmemkv SCRIPTSDIR=${WORKDIR}/utils/docker echo Building on ${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} \ --env http_proxy=${http_proxy} \ --env https_proxy=${https_proxy} \ --env TERM=xterm-256color \ --env WORKDIR=${WORKDIR} \ --env SCRIPTSDIR=${SCRIPTSDIR} \ --env COVERAGE=${COVERAGE} \ --env CI_RUN=${CI_RUN} \ --env TRAVIS=${TRAVIS} \ --env GITHUB_REPO=${GITHUB_REPO} \ --env CI_COMMIT_RANGE=${CI_COMMIT_RANGE} \ --env CI_COMMIT=${CI_COMMIT} \ --env CI_REPO_SLUG=${CI_REPO_SLUG} \ --env CI_BRANCH=${CI_BRANCH} \ --env CI_EVENT_TYPE=${CI_EVENT_TYPE} \ --env GITHUB_ACTIONS=${GITHUB_ACTIONS} \ --env GITHUB_HEAD_REF=${GITHUB_HEAD_REF} \ --env GITHUB_REPO=${GITHUB_REPO} \ --env GITHUB_REPOSITORY=${GITHUB_REPOSITORY} \ --env GITHUB_REF=${GITHUB_REF} \ --env GITHUB_RUN_ID=${GITHUB_RUN_ID} \ --env GITHUB_SHA=${GITHUB_SHA} \ --env DOC_UPDATE_GITHUB_TOKEN=${DOC_UPDATE_GITHUB_TOKEN} \ --env DOC_UPDATE_BOT_NAME=${DOC_UPDATE_BOT_NAME} \ --env DOC_REPO_OWNER=${DOC_REPO_OWNER} \ --env COVERITY_SCAN_TOKEN=${COVERITY_SCAN_TOKEN} \ --env COVERITY_SCAN_NOTIFICATION_EMAIL=${COVERITY_SCAN_NOTIFICATION_EMAIL} \ --env TEST_PACKAGES=${TEST_PACKAGES:-ON} \ --env TESTS_LONG=${TESTS_LONG:-OFF} \ --env TESTS_ASAN=${TESTS_ASAN:-OFF} \ --env TESTS_UBSAN=${TESTS_UBSAN:-OFF} \ --env TEST_TIMEOUT=${TEST_TIMEOUT} \ --env BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} \ --env CHECK_CPP_STYLE=${CHECK_CPP_STYLE:-OFF} \ --env DEFAULT_TEST_DIR=/dev/shm \ --shm-size=4G \ -v ${HOST_WORKDIR}:${WORKDIR} \ -v /etc/localtime:/etc/localtime \ -w ${SCRIPTSDIR} \ ${imageName} ${command} pmemkv-1.5.0/utils/docker/images/000077500000000000000000000000001410000423300166535ustar00rootroot00000000000000pmemkv-1.5.0/utils/docker/images/0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch000066400000000000000000000027641410000423300317460ustar00rootroot00000000000000From d633d3b0a5f03be280efb80a69b9d5ed4e9c4d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Stolarczuk?= Date: Tue, 14 Jul 2020 13:58:34 +0200 Subject: [PATCH] fix generating gcov files and turn-off verbose log --- codecov | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codecov b/codecov index e702ecd..0a2f4d8 100755 --- a/codecov +++ b/codecov @@ -1108,9 +1108,9 @@ then if [ "$ft_gcovout" = "0" ]; then # suppress gcov output - bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -exec $gcov_exe -pb $gcov_arg {} +" >/dev/null 2>&1 || true + bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -execdir $gcov_exe -pb $gcov_arg {} \;" >/dev/null 2>&1 || true else - bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -exec $gcov_exe -pb $gcov_arg {} +" || true + bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -execdir $gcov_exe -pb $gcov_arg {} \;" || true fi else say "${e}==>${x} gcov disabled" @@ -1425,7 +1425,7 @@ do report_len=$(wc -c < "$file") if [ "$report_len" -ne 0 ]; then - say " ${g}+${x} $file ${e}bytes=$(echo "$report_len" | tr -d ' ')${x}" + #say " ${g}+${x} $file ${e}bytes=$(echo "$report_len" | tr -d ' ')${x}" # append to to upload _filename=$(basename "$file") if [ "${_filename##*.}" = 'gcov' ]; -- 2.25.1 pmemkv-1.5.0/utils/docker/images/Dockerfile.archlinux-base-latest000066400000000000000000000052551410000423300250520ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of archlinux-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/archlinux:base MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS archlinux-base ENV OS_VER latest ENV PACKAGE_MANAGER pacman ENV NOTTY 1 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ intel-tbb \ rapidjson" # ndctl's dependencies (optional; ndctl package may be used instead) ARG NDCTL_DEPS="\ automake \ asciidoc \ bash-completion \ pkg-config \ xmlto" # PMDK's dependencies ARG PMDK_DEPS="\ autoconf \ automake \ gdb \ python3 \ which" # libpmemobj-cpp's dependencies ARG LIBPMEMOBJ_CPP_DEPS="\ intel-tbb" # memkind's dependencies ARG MEMKIND_DEPS="\ autoconf \ automake \ numactl" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ sfml" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libunwind" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ file \ perl-text-diff \ pkg-config \ sudo \ whois" # Update the pacman cache and install basic tools RUN pacman -Syu --noconfirm \ && pacman -S --noconfirm \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${NDCTL_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${MEMKIND_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && rm -rf /var/cache/pacman/pkg/* # 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 \ && echo $USERPASS > $PFILE \ && echo $USERPASS >> $PFILE \ && passwd $USER < $PFILE \ && rm -f $PFILE \ && sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.centos-8000066400000000000000000000050441410000423300223070ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of centos-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/centos:8 MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ daxctl-devel \ gdb \ man \ ndctl-devel \ pandoc \ python3 \ rpm-build \ rpm-build-libs \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj++-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic \ tbb-devel" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) # NOTE: glibc is installed as a separate command; see below ARG TESTS_DEPS="\ gdb \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ passwd \ perl-Text-Diff \ pkgconf \ sudo" # Update package repository, extend dnf's package base and install basic tools RUN dnf update -y \ && dnf install -y epel-release \ && dnf install -y 'dnf-command(config-manager)' \ && dnf config-manager --set-enabled powertools \ && dnf update -y \ && dnf install -y --nobest \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && dnf debuginfo-install -y glibc \ && dnf clean all # 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 # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER \ && echo "$USER:$USERPASS" | chpasswd \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.debian-latest000066400000000000000000000044401410000423300233620ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of debian-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/debian:latest MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS debian ENV OS_VER latest 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 # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && 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.5.0/utils/docker/images/Dockerfile.debian-testing000066400000000000000000000044421410000423300235450ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of debian-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/debian:testing MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && 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.5.0/utils/docker/images/Dockerfile.debian-unstable000066400000000000000000000044441410000423300237070ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of debian-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/debian:unstable MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && 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.5.0/utils/docker/images/Dockerfile.fedora-32000066400000000000000000000045101410000423300223260ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.fedoraproject.org/fedora:32 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS fedora ENV OS_VER 32 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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ daxctl-devel \ gdb \ man \ ndctl-devel \ pandoc \ python3 \ rpm-build \ rpm-build-libs \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj++-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic \ tbb-devel" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel \ SFML-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) # NOTE: glibc is installed as a separate command; see below ARG TESTS_DEPS="\ gdb \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ hub \ perl-Text-Diff \ pkgconf \ sudo" # Install basic tools RUN dnf update -y \ && dnf install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && dnf debuginfo-install -y glibc \ && dnf clean all # 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 \ && echo "$USER:$USERPASS" | chpasswd \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.fedora-33000066400000000000000000000045101410000423300223270ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.fedoraproject.org/fedora:33 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS fedora ENV OS_VER 33 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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ daxctl-devel \ gdb \ man \ ndctl-devel \ pandoc \ python3 \ rpm-build \ rpm-build-libs \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj++-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic \ tbb-devel" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel \ SFML-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) # NOTE: glibc is installed as a separate command; see below ARG TESTS_DEPS="\ gdb \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ hub \ perl-Text-Diff \ pkgconf \ sudo" # Install basic tools RUN dnf update -y \ && dnf install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && dnf debuginfo-install -y glibc \ && dnf clean all # 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 \ && echo "$USER:$USERPASS" | chpasswd \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.fedora-34000066400000000000000000000045101410000423300223300ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.fedoraproject.org/fedora:34 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS fedora ENV OS_VER 34 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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ daxctl-devel \ gdb \ man \ ndctl-devel \ pandoc \ python3 \ rpm-build \ rpm-build-libs \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj++-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic \ tbb-devel" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel \ SFML-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) # NOTE: glibc is installed as a separate command; see below ARG TESTS_DEPS="\ gdb \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ hub \ perl-Text-Diff \ pkgconf \ sudo" # Install basic tools RUN dnf update -y \ && dnf install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && dnf debuginfo-install -y glibc \ && dnf clean all # 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 \ && echo "$USER:$USERPASS" | chpasswd \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.fedora-rawhide000066400000000000000000000037021410000423300235270ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of Fedora-based # environment prepared for running pmemkv build and tests. # PMDK (libpmem & libpmemobj) and Valgrind are installed from dnf repo. # # Pull base image FROM registry.fedoraproject.org/fedora:rawhide MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # libpmemobj-cpp's dependencies (optional; libpmemobj++-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic \ libpmemobj-devel \ rpm-build \ tbb-devel" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel \ SFML-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) # NOTE: glibc is installed as a separate command; see below ARG TESTS_DEPS="\ gdb \ libpmem-devel \ libunwind-devel \ pmempool \ valgrind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ perl-Text-Diff \ pkgconf \ sudo" # Update packages and install basic tools RUN dnf update -y \ && dnf install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && dnf debuginfo-install -y glibc \ && dnf clean all # 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 \ && echo "$USER:$USERPASS" | chpasswd \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.opensuse-leap-latest000066400000000000000000000055411410000423300247230ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of opensuse-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.opensuse.org/opensuse/leap:latest MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project # NOTE: tbb-devel is installed from additional repo; see below ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ fdupes \ gdb \ libndctl-devel \ man \ pandoc \ python3 \ rpm-build \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-devel package may be used instead) # NOTE: tbb-devel is installed from additional repo; see below ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ glibc-debuginfo \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ perl-Text-Diff \ pkg-config \ sudo" # Update the OS, packages and install basic tools; # using additional repos for glibc debuginfo and for latest tbb RUN zypper dup -y \ && zypper update -y \ && zypper mr -e repo-debug \ && zypper install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && zypper addrepo https://download.opensuse.org/repositories/Education/openSUSE_Leap_15.1/ education \ && zypper --gpg-auto-import-keys install -y tbb-devel \ && zypper clean all # 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 # Add user ENV USER user ENV USERPASS p1a2s3s4 ENV PFILE ./password RUN useradd -m $USER \ && echo $USERPASS > $PFILE \ && echo $USERPASS >> $PFILE \ && passwd $USER < $PFILE \ && rm -f $PFILE \ && sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers \ && groupadd wheel \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.opensuse-tumbleweed-latest000066400000000000000000000051241410000423300261340ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of opensuse-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.opensuse.org/opensuse/tumbleweed:latest MAINTAINER igor.chorazewicz@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 # Base development packages ARG BASE_DEPS="\ cmake \ gcc \ gcc-c++ \ git \ make" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ memkind-devel \ rapidjson-devel \ tbb-devel" # PMDK's dependencies (optional; libpmemobj-devel package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ fdupes \ gdb \ libndctl-devel \ man \ pandoc \ python3 \ rpm-build \ rpmdevtools \ which" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-devel package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ tbb-devel" # pmem's Valgrind dependencies (optional; valgrind-devel package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ ncurses-devel" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ glibc-debuginfo \ libunwind-devel" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ perl-Text-Diff \ pkg-config \ sudo" # Update the OS, packages and install basic tools; # using additional repo for glibc debuginfo RUN zypper dup -y \ && zypper update -y \ && zypper mr -e repo-debug \ && zypper install -y \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && zypper clean all # 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 # Add user ENV USER user ENV USERPASS p1a2s3s4 ENV PFILE ./password RUN useradd -m $USER \ && echo $USERPASS > $PFILE \ && echo $USERPASS >> $PFILE \ && passwd $USER < $PFILE \ && rm -f $PFILE \ && sed -i 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' /etc/sudoers \ && groupadd wheel \ && gpasswd wheel -a $USER USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.ubuntu-18.04000066400000000000000000000052651410000423300226460ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:18.04 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 18.04 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 # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libtbb-dev \ rapidjson-dev" # ndctl's dependencies ARG NDCTL_DEPS="\ automake \ bash-completion \ ca-certificates \ libkeyutils-dev \ libkmod-dev \ libjson-c-dev \ libudev-dev \ pkg-config \ systemd \ uuid-dev" # PMDK's dependencies ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ pandoc \ python3" # libpmemobj-cpp's dependencies ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # memkind's dependencies ARG MEMKIND_DEPS="\ autoconf \ automake \ numactl \ libnuma-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${NDCTL_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${MEMKIND_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && rm -rf /var/lib/apt/lists/* # Install libndctl COPY install-libndctl.sh install-libndctl.sh RUN ./install-libndctl.sh ubuntu # Install valgrind COPY install-valgrind.sh install-valgrind.sh RUN ./install-valgrind.sh # Install pmdk from sources (because there are no ndctl packages) COPY install-pmdk.sh install-pmdk.sh RUN ./install-pmdk.sh # Install pmdk c++ bindings (also from sources) 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 pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.ubuntu-20.04000066400000000000000000000056461410000423300226420ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # Rapidjson is installed from sources because of package bug. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:20.04 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 20.04 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 ARG SKIP_SCRIPTS_DOWNLOAD # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Codecov - coverage tool (optional) ARG CODECOV_DEPS="\ curl \ llvm" # Coverity - static analysis (optional) ARG COVERITY_DEPS="\ curl \ ruby \ wget" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ clang-format-9 \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${CODECOV_DEPS} \ ${COVERITY_DEPS} \ ${MISC_DEPS} \ && rm -rf /var/lib/apt/lists/* # Install rapidjson from sources COPY install-rapidjson.sh install-rapidjson.sh RUN ./install-rapidjson.sh # 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 # Download scripts required in run-*.sh COPY download-scripts.sh download-scripts.sh COPY 0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch 0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch RUN ./download-scripts.sh # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.ubuntu-20.04_bindings000066400000000000000000000045011410000423300245040ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for executing build and tests # of pmemkv-* bindings repositories. # Rapidjson is installed from sources because of package bug. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:20.04 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 20.04_bindings ENV PACKAGE_MANAGER deb ENV NOTTY 1 ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 # Additional parameters to build docker without building components ARG SKIP_VALGRIND_BUILD ARG SKIP_PMDK_BUILD ARG SKIP_LIBPMEMOBJCPP_BUILD # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev" # Dependencies for compiling and testing pmemkv bindings ARG PMEMKV_BINDINGS_DEPS="\ maven \ npm \ openjdk-8-jdk \ python3-dev \ python3-pip \ ruby-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # Dependencies for compiling libpmemobj-cpp project ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMEMKV_BINDINGS_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${MISC_DEPS} \ && rm -rf /var/lib/apt/lists/* # Install rapidjson from sources COPY install-rapidjson.sh install-rapidjson.sh RUN ./install-rapidjson.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 # Install bindings' dependencies RUN pip3 install pytest setuptools wheel 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.5.0/utils/docker/images/Dockerfile.ubuntu-20.10000066400000000000000000000052051410000423300226260ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:20.10 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 20.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 ARG SKIP_SCRIPTS_DOWNLOAD # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Codecov - coverage tool (optional) ARG CODECOV_DEPS="\ curl \ llvm" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${CODECOV_DEPS} \ ${MISC_DEPS} \ && 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 # Download scripts required in run-*.sh COPY download-scripts.sh download-scripts.sh COPY 0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch 0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch RUN ./download-scripts.sh # Add user ENV USER user ENV USERPASS pass RUN useradd -m $USER -g sudo -p `mkpasswd $USERPASS` USER $USER pmemkv-1.5.0/utils/docker/images/Dockerfile.ubuntu-21.04000066400000000000000000000044701410000423300226350ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:21.04 MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER 21.04 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 ARG SKIP_SCRIPTS_DOWNLOAD # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && 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.5.0/utils/docker/images/Dockerfile.ubuntu-devel000066400000000000000000000044361410000423300232720ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # Dockerfile - a 'recipe' for Docker to build an image of ubuntu-based # environment prepared for running pmemkv build and tests. # # Pull base image FROM registry.hub.docker.com/library/ubuntu:devel MAINTAINER igor.chorazewicz@intel.com # Set required environment variables ENV OS ubuntu ENV OS_VER devel 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 # Base development packages ARG BASE_DEPS="\ cmake \ build-essential \ git" # Dependencies for compiling pmemkv project ARG PMEMKV_DEPS="\ libmemkind-dev \ libtbb-dev \ rapidjson-dev" # PMDK's dependencies (optional; libpmemobj-dev package may be used instead) ARG PMDK_DEPS="\ autoconf \ automake \ debhelper \ devscripts \ gdb \ libdaxctl-dev \ libndctl-dev \ pandoc \ python3" # libpmemobj-cpp's dependencies (optional; libpmemobj-cpp-dev package may be used instead) ARG LIBPMEMOBJ_CPP_DEPS="\ libatomic1 \ libtbb-dev" # pmem's Valgrind dependencies (optional; valgrind package may be used instead) ARG VALGRIND_DEPS="\ autoconf \ automake" # Examples (optional) ARG EXAMPLES_DEPS="\ libncurses5-dev \ libsfml-dev" # Documentation (optional) ARG DOC_DEPS="\ doxygen \ graphviz" # Tests (optional) ARG TESTS_DEPS="\ gdb \ libc6-dbg \ libunwind-dev" # Misc for our builds/CI (optional) ARG MISC_DEPS="\ clang \ libtext-diff-perl \ pkg-config \ sudo \ whois" ENV DEBIAN_FRONTEND noninteractive # Update the apt cache and install basic tools RUN apt-get update \ && apt-get install -y --no-install-recommends \ ${BASE_DEPS} \ ${PMEMKV_DEPS} \ ${PMDK_DEPS} \ ${LIBPMEMOBJ_CPP_DEPS} \ ${VALGRIND_DEPS} \ ${EXAMPLES_DEPS} \ ${DOC_DEPS} \ ${TESTS_DEPS} \ ${MISC_DEPS} \ && 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.5.0/utils/docker/images/README.md000066400000000000000000000017101410000423300201310ustar00rootroot00000000000000# Content Dockerfiles and scripts placed in this directory are intended to be used as development process vehicles and part of continuous integration process. Images built out of those recipes may by used with Docker or podman as development 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=http://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.5.0/utils/docker/images/build-image.sh000077500000000000000000000023431410000423300213730ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # build-image.sh - prepares a Docker image with -based environment for # testing (or dev) purpose, tagged with ${CONTAINER_REG}:${OS}-${OS_VER}-${IMG_VER}, # according to the Dockerfile.${OS}-${OS_VER} file located in the same directory. # IMG_VER is a version of docker image (it usually relates to project's release tag) # and it defaults to "devel". # set -e IMG_VER=${IMG_VER:-devel} TAG="${OS}-${OS_VER}-${IMG_VER}" if [[ -z "${OS}" || -z "${OS_VER}" ]]; then echo "ERROR: The variables OS and OS_VER have to be set " \ "(e.g. OS=fedora, OS_VER=32)." exit 1 fi if [[ -z "${CONTAINER_REG}" ]]; then echo "ERROR: CONTAINER_REG environment variable is not set " \ "(e.g. \"//\")." exit 1 fi echo "Check if the file Dockerfile.${OS}-${OS_VER} exists" if [[ ! -f "Dockerfile.${OS}-${OS_VER}" ]]; then echo "Error: Dockerfile.${OS}-${OS_VER} does not exist." exit 1 fi echo "Build a Docker image tagged with: ${CONTAINER_REG}:${TAG}" docker build -t ${CONTAINER_REG}:${TAG} \ --build-arg http_proxy=$http_proxy \ --build-arg https_proxy=$https_proxy \ -f Dockerfile.${OS}-${OS_VER} . pmemkv-1.5.0/utils/docker/images/download-scripts.sh000077500000000000000000000014351410000423300225110ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation # # download-scripts.sh - downloads specific version of # codecov's bash script to generate and upload reports. # It's useful, since they may break our coverage. # set -e # master: Merge pull request #342 from codecov/revert-proj-name-..., 18.08.2020 CODECOV_VERSION="e877c1280cc6e902101fb5df2981ed1c962da7f0" if [ "${SKIP_SCRIPTS_DOWNLOAD}" ]; then echo "Variable 'SKIP_SCRIPTS_DOWNLOAD' is set; skipping scripts' download" exit fi mkdir -p /opt/scripts git clone https://github.com/codecov/codecov-bash cd codecov-bash git checkout $CODECOV_VERSION git apply ../0001-fix-generating-gcov-files-and-turn-off-verbose-log.patch mv -v codecov /opt/scripts/codecov cd .. rm -rf codecov-bash pmemkv-1.5.0/utils/docker/images/install-bindings-dependencies.sh000077500000000000000000000056661410000423300251140ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # 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 #987 from igchor/num_of_elems, 22.04.2021 PMEMKV_VERSION="09a24d2f456a062a02a190c8d81777d80295337c" # master: Merge pull request #44 from lukaszstolarczuk/update-tr..., 21.11.2019 RUBY_VERSION="3741e3df698245fc8a15822a1aa85b5c211fd332" # common: 1.2.0 release, 02.07.2021 JAVA_VERSION="9a32f9f518198ae575242b448f61514c231b5a60" # master: Merge pull request #55 from lukaszstolarczuk/fix-comment, 03.04.2020 NODEJS_VERSION="76600e002b9d9105d3f46b7cc2bf991931286cec" PREFIX=/usr rm -rf /opt/bindings WORKDIR=$(pwd) # # 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 # Pmemkv at this point is needed only to be linked with JNI. As tests are skipped, # engines are not needed. cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DBUILD_DOC=OFF \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTS=OFF \ -DENGINE_CMAP=OFF \ -DENGINE_VSMAP=OFF \ -DENGINE_VCMAP=OFF \ -DENGINE_STREE=OFF make -j$(nproc) make -j$(nproc) install # # 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/ # # 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 install -Dmaven.test.skip=true mvn dependency:go-offline rm -r ~/.m2/repository/io/pmem mv -v ~/.m2/repository /opt/bindings/java/ # # 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-nodejs npm uninstall cd ${WORKDIR} rm -r pmemkv pmemkv-ruby pmemkv-java pmemkv-nodejs # make the /opt/bindings directory world-readable chmod -R a+r /opt/bindings pmemkv-1.5.0/utils/docker/images/install-libndctl.sh000077500000000000000000000024431410000423300224540ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2021, Intel Corporation # # 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 v69 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.5.0/utils/docker/images/install-libpmemobj-cpp.sh000077500000000000000000000024111410000423300235540ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # install-libpmemobj-cpp.sh [package_type] # - 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^^} #To uppercase echo "PACKAGE_TYPE: ${PACKAGE_TYPE}" # 1.13.0-rc1 release LIBPMEMOBJ_CPP_VERSION="5629c29a2a998ce0c1b45db5885358c35435a7a3" echo "LIBPMEMOBJ_CPP_VERSION: ${LIBPMEMOBJ_CPP_VERSION}" build_dir=$(mktemp -d -t libpmemobj-cpp-XXX) git clone https://github.com/pmem/libpmemobj-cpp --shallow-since=2020-06-01 ${build_dir} pushd ${build_dir} git checkout ${LIBPMEMOBJ_CPP_VERSION} mkdir build cd build # turn off all redundant components cmake .. -DCPACK_GENERATOR="${PACKAGE_TYPE}" -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DBUILD_EXAMPLES=OFF -DBUILD_TESTS=OFF -DBUILD_DOC=OFF -DBUILD_BENCHMARKS=OFF \ -DTESTS_USE_VALGRIND=OFF 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 -iv libpmemobj++*.rpm fi fi popd rm -r ${build_dir} pmemkv-1.5.0/utils/docker/images/install-memkind.sh000077500000000000000000000013641410000423300223060ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # install-memkind.sh - installs memkind from sources; depends on # the system it uses proper installation parameters # set -e # contains better error handling in pmem_allocator MEMKIND_VERSION=64a37d4ca46a8a2419871e859d611fff85d0e843 WORKDIR=$(pwd) git clone https://github.com/memkind/memkind cd ${WORKDIR}/memkind git checkout ${MEMKIND_VERSION} echo "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 echo "cleanup:" cd ${WORKDIR} rm -r memkind pmemkv-1.5.0/utils/docker/images/install-pmdk.sh000077500000000000000000000021631410000423300216130ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # install-pmdk.sh [package_type] - installs PMDK # from DEB/RPM packages if possible. # 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} # master: 1.11.0, 02.07.2021 PMDK_VERSION="8583fcfd68764ac6779e6f93db89b06971b26704" git clone https://github.com/pmem/pmdk cd pmdk git checkout ${PMDK_VERSION} if [ "${PACKAGE_TYPE}" = "" ]; then make DOC=n -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 \ dpkg/libpmemobj_*.deb dpkg/libpmemobj-dev_*.deb \ dpkg/pmreorder_*.deb dpkg/libpmempool_*.deb dpkg/libpmempool-dev_*.deb \ dpkg/libpmemblk_*.deb dpkg/libpmemlog_*.deb dpkg/pmempool_*.deb elif [ "${PACKAGE_TYPE}" = "rpm" ]; then sudo rpm -i rpm/*/pmdk-debuginfo-*.rpm \ rpm/*/libpmem*-*.rpm \ rpm/*/pmreorder-*.rpm \ rpm/*/pmempool-*.rpm fi fi cd .. rm -r pmdk pmemkv-1.5.0/utils/docker/images/install-rapidjson.sh000077500000000000000000000007211410000423300226470ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2020, Intel Corporation # # install-rapidjson.sh - installs rapidjson from sources # set -e git clone https://github.com/Tencent/rapidjson cd rapidjson # master: Merge pull request #1760 from escherstair/fix_ce6_support, 07.08.2020 git checkout "ce81bc9edfe773667a7a4454ba81dac72ed4364c" mkdir build cd build cmake .. make -j$(nproc) sudo make -j$(nproc) install cd ../.. rm -r rapidjson pmemkv-1.5.0/utils/docker/images/install-valgrind.sh000077500000000000000000000014031410000423300224620ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2020, Intel Corporation # # 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 # pmem-3.15: Merge pull request #81 from marcinslusarz/pmem-3.15 git checkout 09f75f69683d862f8456f75484fcdc0dc5508900 # 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.5.0/utils/docker/images/push-image.sh000077500000000000000000000027731410000423300212620ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # push-image.sh - pushes the Docker image tagged as described in # ./build-image.sh, to the ${CONTAINER_REG}. # # The script utilizes ${CONTAINER_REG_USER} and ${CONTAINER_REG_PASS} variables to # log in to the ${CONTAINER_REG}. The variables can be set in the CI's configuration # for automated builds. # set -e IMG_VER=${IMG_VER:-devel} TAG="${OS}-${OS_VER}-${IMG_VER}" if [[ -z "${OS}" ]]; then echo "OS environment variable is not set" exit 1 fi if [[ -z "${OS_VER}" ]]; then echo "OS_VER environment variable is not set" exit 1 fi if [[ -z "${CONTAINER_REG}" ]]; then echo "CONTAINER_REG environment variable is not set" exit 1 fi if [[ -z "${CONTAINER_REG_USER}" || -z "${CONTAINER_REG_PASS}" ]]; then echo "ERROR: variables CONTAINER_REG_USER=\"${CONTAINER_REG_USER}\" and CONTAINER_REG_PASS=\"${CONTAINER_REG_PASS}\"" \ "have to be set properly to allow login to the Container Registry." exit 1 fi # Check if the image tagged with ${CONTAINER_REG}:${TAG} exists locally if [[ ! $(docker images -a | awk -v pattern="^${CONTAINER_REG}:${TAG}\$" \ '$1":"$2 ~ pattern') ]] then echo "ERROR: Docker image tagged ${CONTAINER_REG}:${TAG} does not exists locally." exit 1 fi echo "Log in to the container registry: ${CONTAINER_REG}" echo "${CONTAINER_REG_PASS}" | docker login ghcr.io -u="${CONTAINER_REG_USER}" --password-stdin echo "Push the image to the repository" docker push ${CONTAINER_REG}:${TAG} pmemkv-1.5.0/utils/docker/prepare-for-build.sh000077500000000000000000000055411410000423300212710ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # prepare-for-build.sh - prepare the Docker image for the builds # and defines functions for other scripts. # set -e EXAMPLE_TEST_DIR="/tmp/build_example" PREFIX=/usr ### Helper functions, used in run-*.sh scripts function sudo_password() { echo ${USERPASS} | sudo -Sk $* } function upload_codecov() { printf "\n$(tput setaf 1)$(tput setab 7)COVERAGE ${FUNCNAME[0]} START$(tput sgr 0)\n" # set proper gcov command clang_used=$(cmake -LA -N . | grep CMAKE_CXX_COMPILER | grep clang | wc -c) if [[ ${clang_used} -gt 0 ]]; then gcovexe="llvm-cov gcov" else gcovexe="gcov" fi # run gcov exe, using their bash (remove parsed coverage files, set flag and exit 1 if not successful) # we rely on parsed report on codecov.io; the output is quite long, hence it's disabled using -X flag /opt/scripts/codecov -c -F ${1} -Z -x "${gcovexe}" -X "gcovout" printf "check for any leftover gcov files\n" leftover_files=$(find . -name "*.gcov") if [[ -n "${leftover_files}" ]]; then # display found files and exit with error (they all should be parsed) echo "${leftover_files}" return 1 fi printf "$(tput setaf 1)$(tput setab 7)COVERAGE ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } function compile_example_standalone() { example_name=${1} echo "Compile standalone example: ${example_name}" rm -rf ${EXAMPLE_TEST_DIR} mkdir ${EXAMPLE_TEST_DIR} pushd ${EXAMPLE_TEST_DIR} cmake ${WORKDIR}/examples/${example_name} # exit on error if [[ $? != 0 ]]; then popd return 1 fi make -j$(nproc) popd } function run_example_standalone() { example_name=${1} pool_path=${2} echo "Run standalone example: ${example_name} with path: ${pool_path}" pushd ${EXAMPLE_TEST_DIR} ./${example_name} ${pool_path} # exit on error if [[ $? != 0 ]]; then popd return 1 fi rm -f ${pool_path} popd } function workspace_cleanup() { echo "Cleanup build dirs and example poolset:" pushd ${WORKDIR} rm -rf ${WORKDIR}/build rm -rf ${EXAMPLE_TEST_DIR} pmempool rm -f ${WORKDIR}/examples/example.poolset } ### Additional checks, to be run, when this file is sourced if [[ -z "${WORKDIR}" ]]; then echo "ERROR: The variable WORKDIR has to contain a path to the root " \ "of this project - 'build' sub-directory will be created there." exit 1 fi # this should be run only on CIs if [ "${CI_RUN}" == "YES" ]; then sudo_password chown -R $(id -u).$(id -g) ${WORKDIR} fi || true echo "CMake version:" cmake --version # assign CMake's version to variable(s) - a single number representation for easier comparison CMAKE_VERSION=$(cmake --version | head -n1 | grep -P -o "\d+\.\d+") CMAKE_VERSION_MAJOR=$(echo ${CMAKE_VERSION} | cut -d. -f1) CMAKE_VERSION_MINOR=$(echo ${CMAKE_VERSION} | cut -d. -f2) CMAKE_VERSION_NUMBER=${CMAKE_VERSION_MAJOR}${CMAKE_VERSION_MINOR} pmemkv-1.5.0/utils/docker/pull-or-rebuild-image.sh000077500000000000000000000074071410000423300220530ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2016-2021, Intel Corporation # # pull-or-rebuild-image.sh - rebuilds the Docker image used in the # current build (if necessary) or pulls it from the Container Registry. # Docker image is tagged as described in ./images/build-image.sh, # but IMG_VER defaults in this script to "latest" (just in case it's # used locally without building any images). # # If Docker was rebuilt and all requirements are fulfilled (more details in # push_image function below) image will be pushed to the ${CONTAINER_REG}. # # The script rebuilds the Docker image if: # 1. the Dockerfile for the current OS version (Dockerfile.${OS}-${OS_VER}) # or any .sh script in the Dockerfiles directory were modified and committed, or # 2. "rebuild" param was passed as a first argument to this script. # # The script pulls the Docker image if: # 1. it does not have to be rebuilt (based on committed changes), or # 2. "pull" param was passed as a first argument to this script. # set -e source $(dirname ${0})/set-ci-vars.sh IMG_VER=${IMG_VER:-latest} TAG="${OS}-${OS_VER}-${IMG_VER}" IMAGES_DIR_NAME=images BASE_DIR=utils/docker/${IMAGES_DIR_NAME} if [[ -z "${OS}" || -z "${OS_VER}" ]]; then echo "ERROR: The variables OS and OS_VER have to be set properly " \ "(eg. OS=fedora, OS_VER=32)." exit 1 fi if [[ -z "${CONTAINER_REG}" ]]; then echo "ERROR: CONTAINER_REG environment variable is not set " \ "(e.g. \"//\")." exit 1 fi function build_image() { echo "Building the Docker image for the Dockerfile.${OS}-${OS_VER}" pushd ${IMAGES_DIR_NAME} ./build-image.sh popd } function pull_image() { echo "Pull the image '${CONTAINER_REG}:${TAG}' from the Container Registry." docker pull ${CONTAINER_REG}:${TAG} } function push_image { # Check if the image has to be pushed to the Container Registry: # - only upstream (not forked) repository, # - stable-* or master branch, # - not a pull_request event, # - and PUSH_IMAGE flag was set for current build. if [[ "${CI_REPO_SLUG}" == "${GITHUB_REPO}" \ && (${CI_BRANCH} == stable-* || ${CI_BRANCH} == master) \ && ${CI_EVENT_TYPE} != "pull_request" \ && ${PUSH_IMAGE} == "1" ]] then echo "The image will be pushed to the Container Registry: ${CONTAINER_REG}" pushd ${IMAGES_DIR_NAME} ./push-image.sh popd else echo "Skip pushing the image to the Container Registry." fi } # If "rebuild" or "pull" are passed to the script as param, force rebuild/pull. if [[ "${1}" == "rebuild" ]]; then build_image push_image exit 0 elif [[ "${1}" == "pull" ]]; then pull_image exit 0 fi # Determine if we need to rebuild the image or just pull it from # the Container Registry, based on committed changes. if [ -n "${CI_COMMIT_RANGE}" ]; then commits=$(git rev-list ${CI_COMMIT_RANGE}) else commits=${CI_COMMIT} fi if [[ -z "${commits}" ]]; then echo "'commits' variable is empty. Docker image will be pulled." fi echo "Commits in the commit range:" for commit in ${commits}; do echo ${commit}; done echo "Files modified within the commit range:" files=$(for commit in ${commits}; do git diff-tree --no-commit-id --name-only \ -r ${commit}; done | sort -u) for file in ${files}; do echo ${file}; done # 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 build_image push_image exit 0 fi done # Getting here means rebuilding the Docker image isn't required (based on changed files). # Pull the image from the Container Registry or rebuild anyway, if pull fails. if ! pull_image; then build_image push_image fi pmemkv-1.5.0/utils/docker/run-bindings.sh000077500000000000000000000055671410000423300203610ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # run-bindings.sh - checks bindings' building and installation # set -e source `dirname ${0}`/prepare-for-build.sh # master: Merge pull request #44 from lukaszstolarczuk/update-tr..., 21.11.2019 RUBY_VERSION="3741e3df698245fc8a15822a1aa85b5c211fd332" # common: 1.2.0 release, 02.07.2021 JAVA_VERSION="9a32f9f518198ae575242b448f61514c231b5a60" # master: Merge pull request #55 from lukaszstolarczuk/fix-comment, 03.04.2020 NODEJS_VERSION="76600e002b9d9105d3f46b7cc2bf991931286cec" # master: ver. 1.0, 03.03.2020 PYTHON_VERSION="094bc84fdabff81c2eb2017d32caad2582835f90" # build and install pmemkv cd ${WORKDIR} mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DBUILD_DOC=OFF \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTS=OFF make -j$(nproc) sudo_password -S make -j$(nproc) install echo echo "##################################################################" echo "### Verifying building and installing of the pmemkv-ruby bindings " echo "##################################################################" cd ${WORKDIR} 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 cd .. rm -rf pmemkv-ruby echo echo "##################################################################" echo "### Verifying building and installing of the pmemkv-java bindings " echo "##################################################################" cd ${WORKDIR} 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 install -e cd .. rm -rf pmemkv-java echo echo "####################################################################" echo "### Verifying building and installing of the pmemkv-nodejs bindings " echo "####################################################################" cd ${WORKDIR} 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 cd .. rm -rf pmemkv-nodejs echo echo "####################################################################" echo "### Verifying building and installing of the pmemkv-python bindings " echo "####################################################################" cd ${WORKDIR} git clone https://github.com/pmem/pmemkv-python.git cd pmemkv-python git checkout ${PYTHON_VERSION} python3 setup.py bdist_wheel pip3 install dist/pmemkv-*.whl --user cd tests python3 -X faulthandler -m pytest -v pmemkv_tests.py python3 -X faulthandler -m pytest -v nontrivial_data_tests.py cd ../examples PMEM_IS_PMEM_FORCE=1 python3 basic_example.py cd ../.. rm -rf pmemkv-python pmemkv-1.5.0/utils/docker/run-build.sh000077500000000000000000000216141410000423300176520ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # run-build.sh - is called inside a Docker container, # starts pmemkv builds (defined by the CI jobs) with tests. # set -e source `dirname ${0}`/prepare-for-build.sh # params set for the file (if not previously set, the right-hand param is used) TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} CHECK_CPP_STYLE=${CHECK_CPP_STYLE:-ON} TESTS_LONG=${TESTS_LONG:-OFF} TESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM:-ON} TESTS_ASAN=${TESTS_ASAN:-OFF} TESTS_UBSAN=${TESTS_UBSAN:-OFF} TEST_TIMEOUT=${TEST_TIMEOUT:-600} ############################################################################### # BUILD tests_gcc_debug_cpp11 ############################################################################### function tests_gcc_debug_cpp11() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" mkdir build cd build CC=gcc CXX=g++ \ cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=${TEST_DIR} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCOVERAGE=${COVERAGE} \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DCHECK_CPP_STYLE=${CHECK_CPP_STYLE} \ -DTESTS_LONG=${TESTS_LONG} \ -DDEVELOPER_MODE=1 \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DTESTS_PMEMOBJ_DRD_HELGRIND=1 \ -DCXX_STANDARD=11 \ -DUSE_ASAN=${TESTS_ASAN} \ -DUSE_UBSAN=${TESTS_UBSAN} make -j$(nproc) ctest -E "_memcheck|_drd|_helgrind|_pmemcheck|_pmreorder" --timeout ${TEST_TIMEOUT} --output-on-failure if [ "${COVERAGE}" == "1" ]; then upload_codecov gcc_debug_cpp11 fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD tests_gcc_debug_cpp14 ############################################################################### function tests_gcc_debug_cpp14() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" mkdir build cd build CC=gcc CXX=g++ \ cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=${TEST_DIR} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCOVERAGE=${COVERAGE} \ -DENGINE_CSMAP=1 \ -DENGINE_RADIX=1 \ -DENGINE_ROBINHOOD=1 \ -DENGINE_DRAM_VCMAP=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DTESTS_LONG=${TESTS_LONG} \ -DDEVELOPER_MODE=1 \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DTESTS_PMEMOBJ_DRD_HELGRIND=1 \ -DCXX_STANDARD=14 \ -DUSE_ASAN=${TESTS_ASAN} \ -DUSE_UBSAN=${TESTS_UBSAN} make -j$(nproc) ctest -E "_memcheck|_drd|_helgrind|_pmemcheck|_pmreorder" --timeout ${TEST_TIMEOUT} --output-on-failure if [ "${COVERAGE}" == "1" ]; then upload_codecov gcc_debug_cpp14 fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD tests_gcc_debug_cpp14_valgrind_other ############################################################################### function tests_gcc_debug_cpp14_valgrind_other() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" mkdir build cd build CC=gcc CXX=g++ \ cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=${TEST_DIR} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCOVERAGE=${COVERAGE} \ -DENGINE_CSMAP=1 \ -DENGINE_RADIX=1 \ -DENGINE_ROBINHOOD=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DTESTS_LONG=${TESTS_LONG} \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DCXX_STANDARD=14 make -j$(nproc) ctest -R "_helgrind|_pmemcheck|_pmreorder" --timeout ${TEST_TIMEOUT} --output-on-failure if [ "${COVERAGE}" == "1" ]; then upload_codecov gcc_debug_cpp14_valgrind_other fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD tests_gcc_debug_cpp14_valgrind_memcheck_drd ############################################################################### function tests_gcc_debug_cpp14_valgrind_memcheck_drd() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" mkdir build cd build CC=gcc CXX=g++ \ cmake .. -DCMAKE_BUILD_TYPE=Debug \ -DTEST_DIR=${TEST_DIR} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCOVERAGE=${COVERAGE} \ -DENGINE_CSMAP=1 \ -DENGINE_RADIX=1 \ -DENGINE_ROBINHOOD=1 \ -DENGINE_DRAM_VCMAP=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DTESTS_LONG=${TESTS_LONG} \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DTESTS_PMEMOBJ_DRD_HELGRIND=1 \ -DCXX_STANDARD=14 make -j$(nproc) ctest -R "_memcheck|_drd" --timeout ${TEST_TIMEOUT} --output-on-failure if [ "${COVERAGE}" == "1" ]; then upload_codecov gcc_debug_cpp14_valgrind_memcheck_drd fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD tests_clang_release_cpp20 llvm ############################################################################### function tests_clang_release_cpp20() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" # CXX_STANDARD==20 is supported since CMake 3.12 if [ ${CMAKE_VERSION_NUMBER} -lt 312 ]; then echo "ERROR: C++20 is supported in CMake since 3.12, installed version: ${CMAKE_VERSION}" exit 1 fi mkdir build cd build CC=clang CXX=clang++ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DTEST_DIR=${TEST_DIR} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DCOVERAGE=${COVERAGE} \ -DENGINE_RADIX=1 \ -DENGINE_ROBINHOOD=1 \ -DENGINE_DRAM_VCMAP=1 \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} \ -DTESTS_LONG=${TESTS_LONG} \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DTESTS_PMEMOBJ_DRD_HELGRIND=1 \ -DDEVELOPER_MODE=1 \ -DCXX_STANDARD=20 \ -DUSE_ASAN=${TESTS_ASAN} \ -DUSE_UBSAN=${TESTS_UBSAN} make -j$(nproc) ctest -E "_memcheck|_drd|_helgrind|_pmemcheck|_pmreorder" --timeout ${TEST_TIMEOUT} --output-on-failure if [ "${COVERAGE}" == "1" ]; then upload_codecov clang_release_cpp20 fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD test_release_installation ############################################################################### function test_release_installation() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" mkdir build cd build CC=gcc CXX=g++ \ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DENGINE_RADIX=1 \ -DBUILD_TESTS=0 \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} make -j$(nproc) sudo_password -S make -j$(nproc) install echo "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 echo "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 echo "Expect failure - non-existing path is passed:" run_example_standalone pmemkv_open_cpp /non-existing/path && exit 1 echo "Transaction C++ example:" compile_example_standalone pmemkv_transaction_cpp pmempool create -l "pmemkv_radix" obj ${WORKDIR}/examples/example.poolset run_example_standalone pmemkv_transaction_cpp ${WORKDIR}/examples/example.poolset echo "Transaction C example:" compile_example_standalone pmemkv_transaction_c pmempool rm -f ${WORKDIR}/examples/example.poolset pmempool create -l "pmemkv_radix" obj ${WORKDIR}/examples/example.poolset run_example_standalone pmemkv_transaction_c ${WORKDIR}/examples/example.poolset echo "Uninstall libraries" cd ${WORKDIR}/build sudo_password -S make uninstall workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } # Main: cd ${WORKDIR} echo "### Cleaning workspace" workspace_cleanup echo echo "### Making sure there is no libpmemkv currently installed" echo "---------------------------- Error expected! ------------------------------" compile_example_standalone pmemkv_basic_cpp && exit 1 echo "---------------------------------------------------------------------------" echo "Run build steps passed as script arguments:" build_steps=$@ echo "Defined build steps: ${build_steps}" if [[ -z "${build_steps}" ]]; then echo "ERROR: The variable build_steps with selected builds to run is not set!" exit 1 fi for build in ${build_steps} do ${build} done pmemkv-1.5.0/utils/docker/run-compatibility.sh000077500000000000000000000043031410000423300214200ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation # # run-compatibility.sh - verify compatibility between versions # set -e TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} INSTALL_PREFIX=/opt source `dirname ${0}`/prepare-for-build.sh function build_and_install_pmemkv() { version=${1} git checkout ${version} mkdir ${WORKDIR}/build cd ${WORKDIR}/build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_TESTS=OFF \ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}/pmemkv-${version} make -j$(nproc) sudo_password -S make -j$(nproc) install workspace_cleanup } function verify_compatibility() { version=${1} echo echo "##################################################################" echo "### Verifying compatibility with ${version}" echo "##################################################################" mkdir ${WORKDIR}/build cd ${WORKDIR}/build mkdir pmemkv-HEAD cd pmemkv-HEAD PKG_CONFIG_PATH=${INSTALL_PREFIX}/pmemkv-HEAD/lib64/pkgconfig cmake ../../tests/compatibility make -j$(nproc) cd .. mkdir pmemkv-${version} cd pmemkv-${version} PKG_CONFIG_PATH=${INSTALL_PREFIX}/pmemkv-${version}/lib64/pkgconfig cmake ../../tests/compatibility make -j$(nproc) cd ../.. PMEM_IS_PMEM_FORCE=1 ${WORKDIR}/tests/compatibility/cmap.sh ${WORKDIR}/build/pmemkv-HEAD/cmap_compatibility ${WORKDIR}/build/pmemkv-${version}/cmap_compatibility ${TEST_DIR}/testfile workspace_cleanup } ## Main: # Fetch git history if clone is shallow [ -f ${WORKDIR}/.git/shallow ] && git fetch --unshallow --tags current_version=$(git describe --all) echo "Current pmemkv version: ${current_version}" echo "Build and install current pmemkv's version - 'HEAD'" build_and_install_pmemkv "HEAD" echo "Build and install older pmemkv's versions" build_and_install_pmemkv "1.0.2" build_and_install_pmemkv "1.1" build_and_install_pmemkv "1.2" build_and_install_pmemkv "1.3" build_and_install_pmemkv "1.4" # checkout HEAD/current version again git checkout ${current_version} echo "Test compatibility of previous versions with current one" verify_compatibility "1.0.2" verify_compatibility "1.1" verify_compatibility "1.2" verify_compatibility "1.3" verify_compatibility "1.4" pmemkv-1.5.0/utils/docker/run-coverity.sh000077500000000000000000000042141410000423300204140ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2021, Intel Corporation # # run-coverity.sh - runs the Coverity scan build # set -e if [[ "${CI_REPO_SLUG}" != "${GITHUB_REPO}" \ && ( "${COVERITY_SCAN_NOTIFICATION_EMAIL}" == "" \ || "${COVERITY_SCAN_TOKEN}" == "" ) ]]; then echo echo "Skipping Coverity build:"\ "COVERITY_SCAN_TOKEN=\"${COVERITY_SCAN_TOKEN}\" or"\ "COVERITY_SCAN_NOTIFICATION_EMAIL="\ "\"${COVERITY_SCAN_NOTIFICATION_EMAIL}\" is not set" exit 0 fi # Prepare build environment source `dirname ${0}`/prepare-for-build.sh CERT_FILE=/etc/ssl/certs/ca-certificates.crt TEMP_CF=$(mktemp) cp ${CERT_FILE} ${TEMP_CF} # Download Coverity certificate echo -n | openssl s_client -connect scan.coverity.com:443 | \ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | \ tee -a ${TEMP_CF} sudo_password mv ${TEMP_CF} ${CERT_FILE} cd ${WORKDIR} mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Debug export COVERITY_SCAN_PROJECT_NAME="${CI_REPO_SLUG}" export COVERITY_SCAN_BRANCH_PATTERN="master" export COVERITY_SCAN_BUILD_COMMAND="make -j$(nproc)" # # Run the Coverity scan # # The 'travisci_build_coverity_scan.sh' script requires the following # environment variables to be set: # - TRAVIS_BRANCH - has to contain the name of the current branch # - TRAVIS_PULL_REQUEST - has to be set to 'true' in case of pull requests # export TRAVIS_BRANCH=${CI_BRANCH} [ "${CI_EVENT_TYPE}" == "pull_request" ] && export TRAVIS_PULL_REQUEST="true" # 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.5.0/utils/docker/run-doc-update.sh000077500000000000000000000067621410000423300206070ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2018-2021, Intel Corporation # # run-doc-update.sh - is called inside a Docker container, # to build docs for 'valid branches' and to create a pull request # with and update of doxygen and manpage files (on gh-pages). # set -e if [[ -z "${DOC_UPDATE_GITHUB_TOKEN}" || -z "${DOC_UPDATE_BOT_NAME}" || -z "${DOC_REPO_OWNER}" ]]; then echo "To build documentation and upload it as a Github pull request, variables " \ "'DOC_UPDATE_BOT_NAME', 'DOC_REPO_OWNER' and 'DOC_UPDATE_GITHUB_TOKEN' have to " \ "be provided. For more details please read CONTRIBUTING.md" exit 0 fi # Set up required variables BOT_NAME=${DOC_UPDATE_BOT_NAME} DOC_REPO_OWNER=${DOC_REPO_OWNER} REPO_NAME=${REPO:-"pmemkv"} export GITHUB_TOKEN=${DOC_UPDATE_GITHUB_TOKEN} # export for hub command REPO_DIR=$(mktemp -d -t pmemkv-XXX) ARTIFACTS_DIR=$(mktemp -d -t ARTIFACTS-XXX) # Only 'master' or 'stable-*' branches are valid; determine docs location dir on gh-pages branch TARGET_BRANCH=${CI_BRANCH} if [[ "${TARGET_BRANCH}" == "master" ]]; then TARGET_DOCS_DIR="master" elif [[ ${TARGET_BRANCH} == stable-* ]]; then TARGET_DOCS_DIR=v$(echo ${TARGET_BRANCH} | cut -d"-" -f2 -s) else echo "Skipping docs build, this script should be run only on master or stable-* branches." echo "TARGET_BRANCH is set to: \'${TARGET_BRANCH}\'." exit 0 fi if [ -z "${TARGET_DOCS_DIR}" ]; then echo "ERROR: Target docs location for branch: ${TARGET_BRANCH} is not set." exit 1 fi ORIGIN="https://${GITHUB_TOKEN}@github.com/${BOT_NAME}/${REPO_NAME}" UPSTREAM="https://github.com/${DOC_REPO_OWNER}/${REPO_NAME}" pushd ${REPO_DIR} echo "Clone repo:" git clone ${ORIGIN} ${REPO_DIR} cd ${REPO_DIR} git remote add upstream ${UPSTREAM} git config --local user.name ${BOT_NAME} git config --local user.email "${BOT_NAME}@intel.com" hub config --global hub.protocol https git remote update git checkout -B ${TARGET_BRANCH} upstream/${TARGET_BRANCH} echo "Build docs:" mkdir -p ${REPO_DIR}/build cd ${REPO_DIR}/build cmake .. -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF make -j$(nproc) doc cp ${REPO_DIR}/build/doc/man/tmp/*.md ${REPO_DIR}/doc/ cp -r ${REPO_DIR}/doc ${ARTIFACTS_DIR}/ cp -r ${REPO_DIR}/build/doc/cpp_html ${ARTIFACTS_DIR}/ cd ${REPO_DIR} # Checkout gh-pages and copy docs GH_PAGES_NAME="${TARGET_DOCS_DIR}-gh-pages-update" git checkout -B ${GH_PAGES_NAME} upstream/gh-pages git clean -dfx # Clean old content, since some files might have been deleted rm -rf ./${TARGET_DOCS_DIR} mkdir -p ./${TARGET_DOCS_DIR}/manpages/ mkdir -p ./${TARGET_DOCS_DIR}/doxygen/ # copy all manpages (with format like .
.md) cp -f ${ARTIFACTS_DIR}/doc/*.*.md ./${TARGET_DOCS_DIR}/manpages/ cp -fr ${ARTIFACTS_DIR}/cpp_html/* ./${TARGET_DOCS_DIR}/doxygen/ # Fix the title tag in manpages: # 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' ./${TARGET_DOCS_DIR}/manpages/*.md echo "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_NAME} # 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 ${DOC_REPO_OWNER}:gh-pages -h ${BOT_NAME}:${GH_PAGES_NAME} \ -m "doc: automatic gh-pages docs update" && true popd pmemkv-1.5.0/utils/docker/run-test-building.sh000077500000000000000000000145671410000423300213360ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2021, Intel Corporation # # run-test-building.sh - is called inside a Docker container, # starts testing of pmemkv building # and automatic update of the documentation. # set -e source `dirname ${0}`/prepare-for-build.sh # params set for the file (if not previously set, the right-hand param is used) TEST_DIR=${PMEMKV_TEST_DIR:-${DEFAULT_TEST_DIR}} TEST_PACKAGES=${TEST_PACKAGES:-ON} BUILD_JSON_CONFIG=${BUILD_JSON_CONFIG:-ON} TESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM:-ON} TESTS_ASAN=${TESTS_ASAN:-OFF} TESTS_UBSAN=${TESTS_UBSAN:-OFF} TEST_TIMEOUT=${TEST_TIMEOUT:-600} ############################################################################### # BUILD test_gcc_cpp20 ############################################################################### function test_gcc_cpp20() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" CWD=$(pwd) mkdir ${WORKDIR}/build cd ${WORKDIR}/build CC=gcc 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 \ -DTESTS_USE_FORCED_PMEM=${TESTS_USE_FORCED_PMEM} \ -DCXX_STANDARD=20 \ -DUSE_ASAN=${TESTS_ASAN} \ -DUSE_UBSAN=${TESTS_UBSAN} make -j$(nproc) # Run basic tests ctest -R "SimpleTest" --output-on-failure --timeout ${TEST_TIMEOUT} if [ "${COVERAGE}" == "1" ]; then upload_codecov test_gcc_cpp20 fi workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } ############################################################################### # BUILD test_building_of_packages ############################################################################### function test_building_of_packages() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" # 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 pool compile_example_standalone pmemkv_basic_cpp run_example_standalone pmemkv_basic_cpp pool # 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 workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } # helper function to check building with specified CMake flag function build_with_flags() { printf "\n$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} START$(tput sgr 0)\n" CMAKE_FLAGS_AND_SETTINGS=$@ echo echo "##############################################################" echo "### Verifying building with flag: ${CMAKE_FLAGS_AND_SETTINGS}" echo "##############################################################" mkdir ${WORKDIR}/build cd ${WORKDIR}/build cmake .. ${CMAKE_FLAGS_AND_SETTINGS} make -j$(nproc) # list all tests in this build ctest -N workspace_cleanup printf "$(tput setaf 1)$(tput setab 7)BUILD ${FUNCNAME[0]} END$(tput sgr 0)\n\n" } # Main: workspace_cleanup cd ${WORKDIR} # CXX_STANDARD==20 is supported since CMake 3.12 if [ ${CMAKE_VERSION_NUMBER} -ge 312 ]; then test_gcc_cpp20 fi echo echo "##############################################################" echo "### Verifying if each engine is building properly" echo "##############################################################" engines_flags=( ENGINE_VSMAP ENGINE_VCMAP ENGINE_CMAP ENGINE_CSMAP ENGINE_STREE ENGINE_TREE3 ENGINE_RADIX ENGINE_ROBINHOOD ENGINE_DRAM_VCMAP # 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 .. -DCXX_STANDARD=14 \ -DENGINE_VSMAP=OFF \ -DENGINE_VCMAP=OFF \ -DENGINE_CMAP=OFF \ -DENGINE_CSMAP=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 --timeout ${TEST_TIMEOUT} workspace_cleanup done echo echo "##############################################################" echo "### Verifying building of all engines" echo "##############################################################" mkdir ${WORKDIR}/build cd ${WORKDIR}/build cmake .. -DCXX_STANDARD=14 \ -DENGINE_VSMAP=ON \ -DENGINE_VCMAP=ON \ -DENGINE_CMAP=ON \ -DENGINE_CSMAP=ON \ -DENGINE_STREE=ON \ -DENGINE_TREE3=ON \ -DENGINE_RADIX=ON \ -DENGINE_ROBINHOOD=ON \ -DENGINE_DRAM_VCMAP=ON \ -DBUILD_JSON_CONFIG=${BUILD_JSON_CONFIG} make -j$(nproc) # list all tests in this build ctest -N workspace_cleanup echo echo "##############################################################" echo "### Verifying build with specific CMake flags" echo "##############################################################" build_with_flags -DBUILD_JSON_CONFIG=OFF -DTESTS_JSON=OFF # building of packages should be verified only if PACKAGE_MANAGER equals 'rpm' or 'deb' case ${PACKAGE_MANAGER} in rpm|deb) [ "${TEST_PACKAGES}" == "ON" ] && test_building_of_packages ;; *) echo "Notice: skipping building of packages because PACKAGE_MANAGER is not equal 'rpm' nor 'deb' ..." ;; esac pmemkv-1.5.0/utils/docker/set-ci-vars.sh000077500000000000000000000066541410000423300201150ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2020-2021, Intel Corporation # # set-ci-vars.sh -- set CI variables common for both: # Travis and GitHub Actions CIs # set -e 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) RANGE_END="HEAD" if [ -n "${GITHUB_ACTIONS}" ] && [ "${GITHUB_EVENT_NAME}" == "pull_request" ] && [ "${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_COMMIT=$(git log --pretty=%H -2 | tail -n1) LAST_MERGE=$(git log --merges --pretty=%H -2 | tail -n1) # If still the last commit is a merge commit it means we're manually # merging changes (probably back from stable branch). We have to use # left parent of the merge and the current commit for COMMIT_RANGE. if [ "${LAST_MERGE}" == "${LAST_COMMIT}" ]; then LAST_MERGE=$(git log --merges --pretty=%P -2 | tail -n1 | cut -d" " -f1) RANGE_END=${LAST_COMMIT} fi elif [ "${LAST_MERGE}" == "${LAST_COMMIT}" ] && ([ "${TRAVIS_EVENT_TYPE}" == "push" ] || [ "${GITHUB_EVENT_NAME}" == "push" ]); then # Other case in which last commit equals last merge, is when committing # a manual merge. Push events don't set proper COMMIT_RANGE. # It has to be then set: from merge's left parent to the current commit. LAST_MERGE=$(git log --merges --pretty=%P -1 | cut -d" " -f1) fi if [ "${LAST_MERGE}" == "" ]; then # possible in case of shallow clones # or new repos with no merge commits yet # - pick up the first commit LAST_MERGE=$(git log --pretty=%H | tail -n1) fi COMMIT_RANGE="${LAST_MERGE}..${RANGE_END}" # make sure it works now if ! git rev-list ${COMMIT_RANGE} >/dev/null; then COMMIT_RANGE="" fi echo ${COMMIT_RANGE} } COMMIT_RANGE_FROM_LAST_MERGE=$(get_commit_range_from_last_merge) if [ -n "${TRAVIS}" ]; then CI_COMMIT=${TRAVIS_COMMIT} CI_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE/.../..}" CI_BRANCH=${TRAVIS_BRANCH} CI_EVENT_TYPE=${TRAVIS_EVENT_TYPE} CI_REPO_SLUG=${TRAVIS_REPO_SLUG} # CI_COMMIT_RANGE is usually invalid for force pushes - fix it when used # with non-upstream repository if [ -n "${CI_COMMIT_RANGE}" -a "${CI_REPO_SLUG}" != "${GITHUB_REPO}" ]; then if ! git rev-list ${CI_COMMIT_RANGE}; then CI_COMMIT_RANGE=${COMMIT_RANGE_FROM_LAST_MERGE} fi fi case "${TRAVIS_CPU_ARCH}" in "amd64") CI_CPU_ARCH="x86_64" ;; *) CI_CPU_ARCH=${TRAVIS_CPU_ARCH} ;; esac elif [ -n "${GITHUB_ACTIONS}" ]; then CI_COMMIT=${GITHUB_SHA} CI_COMMIT_RANGE=${COMMIT_RANGE_FROM_LAST_MERGE} CI_BRANCH=$(echo ${GITHUB_REF} | cut -d'/' -f3) CI_REPO_SLUG=${GITHUB_REPOSITORY} CI_CPU_ARCH="x86_64" # GitHub Actions supports only x86_64 case "${GITHUB_EVENT_NAME}" in "schedule") CI_EVENT_TYPE="cron" ;; *) CI_EVENT_TYPE=${GITHUB_EVENT_NAME} ;; esac else CI_COMMIT=$(git log --pretty=%H -1) CI_COMMIT_RANGE=${COMMIT_RANGE_FROM_LAST_MERGE} CI_CPU_ARCH="x86_64" fi export CI_COMMIT=${CI_COMMIT} export CI_COMMIT_RANGE=${CI_COMMIT_RANGE} export CI_BRANCH=${CI_BRANCH} export CI_EVENT_TYPE=${CI_EVENT_TYPE} export CI_REPO_SLUG=${CI_REPO_SLUG} export CI_CPU_ARCH=${CI_CPU_ARCH} echo CI_COMMIT=${CI_COMMIT} echo CI_COMMIT_RANGE=${CI_COMMIT_RANGE} echo CI_BRANCH=${CI_BRANCH} echo CI_EVENT_TYPE=${CI_EVENT_TYPE} echo CI_REPO_SLUG=${CI_REPO_SLUG} echo CI_CPU_ARCH=${CI_CPU_ARCH} pmemkv-1.5.0/utils/jenkins/000077500000000000000000000000001410000423300156005ustar00rootroot00000000000000pmemkv-1.5.0/utils/jenkins/Jenkinsfile000066400000000000000000000142031410000423300177640ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2021, Intel Corporation */ /* Jenkinsfile - Scripts to create pmemkv and pmemkv_matrix jobs - to run with initial jenkins job. */ /* define common properties of pipelines: */ description_param = ''' string { name('DESCRIPTION') defaultValue('') trim(true) description('Optional description for this execution') } ''' label_param = ''' string { name('LABEL') defaultValue('fedora') trim(true) description("Name of the node or group to run job on: rhel8_2,\ openSUSE15_2, fedora32, ubuntu20_04, ubuntu19.10, debian10, etc.") } ''' repo_url_param = ''' string { name('REPO_URL') defaultValue('https://github.com/pmem/pmemkv.git') trim(true) description("Git repository address") } ''' branch_param = ''' string { name('BRANCH') defaultValue('master') trim(true) description("Repository's branch") } ''' test_options_param = ''' string { name('TEST_OPTIONS') defaultValue('tests_gcc_debug_cpp11 tests_gcc_debug_cpp14 test_release_installation tests_clang_release_cpp20') trim(true) description("Test builds, as defined in run-build.sh") } ''' type_param = ''' choiceParam('TEST_TYPE', ['normal', 'building', 'compatibility'], 'Type of tests') ''' coverage_param = ''' choiceParam('COVERAGE', ['no', 'yes'], 'Enable code coverage') ''' test_device = ''' choiceParam('DEVICE_TYPE', ['PMEM', 'DAX', 'NONE'], 'Select tested device type. For PMEM and DAX,\ capable persistent memory must be present on the server.') ''' gitlab_connection = ''' properties { gitLabConnection { gitLabConnection('gitlabIT') } } ''' email_options_param = ''' string { name('EMAIL_RECIPIENTS') defaultValue('') trim(true) description("Recipients of the e-mail sent after execution, separated by the comma.") } booleanParam('SEND_RESULTS', true, 'Uncheck to disable sending email with report after execution') ''' snapshot_option_param = ''' choiceParam('SNAP_OPT', ['JENKINS_LATEST', 'LATEST', 'CHOOSE', 'SKIP'], 'Name of a snapshot to restore:\ JENKINS_LATEST - choose snapshot described with Jenkins_backup_latest; LATEST - choose latest snapshot;\ CHOOSE - use SNAP_OPT_NAME; SKIP - skip restoring snapshot. Snapshot must be present on the target server. ') string { name('SNAP_OPT_NAME') defaultValue('') trim(true) description("If 'CHOOSE' is selected, type the valid name of a Timeshift snapshot") } ''' ssh_creds_option_param = ''' string { name('SSH_CRED') defaultValue('test-user-sshkey') trim(true) description("Enter valid ssh credentials stored in Jenkins") } ''' downstream_job_option_param = ''' string { name('DOWNSTREAM_JOB') defaultValue('pmemkv_linux') trim(true) description("Downstream job to run after successful snapshot restore.") } ''' /* Branch with Jenkins libraries to pull: */ jenkins_files_branch_source = 'master' /* Loads pipeline scripts from repository */ def remote_definition(current_job_script_path) { return """ definition { cpsScm { scm { git { remote { url('https://github.com/pmem/pmemkv.git') branch('${jenkins_files_branch_source}') scriptPath('${current_job_script_path}') } } } } } """ } environmental_variables = """ environmentVariables { envs( api_lib: "jenkins_files/utils/jenkins/lib/api.groovy", lib_path: "jenkins_files/utils/jenkins/lib/", scripts_path: "jenkins_files/utils/jenkins/scripts/", jenkins_files_repo: "https://github.com/pmem/pmemkv.git", jenkins_files_branch: "*/${jenkins_files_branch_source}", info_addr: "https://pmem-val-jenkins.pact.intel.com/" ) } """ triggers = ''' triggers { gitlabPush { buildOnMergeRequestEvents(true) buildOnPushEvents(true) enableCiSkip(false) setBuildDescription(false) rebuildOpenMergeRequest('never') } } ''' node { stage('pmemkv_linux'){ jobDsl scriptText: """pipelineJob("pmemkv_linux") { parameters { ${label_param} ${branch_param} ${type_param} ${test_options_param} ${test_device} ${repo_url_param} ${description_param} ${coverage_param} ${email_options_param} } ${gitlab_connection} ${environmental_variables} ${triggers} ${remote_definition 'utils/jenkins/pmemkv_linux.jenkins'} }""" } stage('pmemkv_linux_timeshift'){ jobDsl scriptText: """pipelineJob("pmemkv_linux_timeshift") { parameters { ${label_param} ${snapshot_option_param} ${ssh_creds_option_param} ${downstream_job_option_param} ${branch_param} ${type_param} ${test_options_param} ${test_device} ${repo_url_param} ${description_param} ${coverage_param} ${email_options_param} } ${gitlab_connection} ${environmental_variables} ${triggers} ${remote_definition 'utils/jenkins/restore_snapshot.jenkins'} }""" } stage('pmemkv_linux_matrix'){ jobDsl scriptText: """matrixJob("pmemkv_linux_matrix") { parameters { matrixCombinationsParam('COMBINATIONS', '', 'choose which combinations to run') ${branch_param} ${coverage_param} ${repo_url_param} ${test_options_param} ${test_device} ${description_param} ${email_options_param} } axes { text('DISTRO', 'ubuntu19_10', 'ubuntu20_04', 'fedora31') text('TYPE', 'normal', 'building', 'compatibility') label('master', 'master') } steps { downstreamParameterized { trigger("pmemkv_linux") { parameters { predefinedProp('COV', '\${COVERAGE}') predefinedProp('TEST_TYPE', '\${TYPE}') predefinedProp('LABEL', '\${DISTRO}') predefinedProp('BRANCH', '\${BRANCH}') predefinedProp('TEST_OPTIONS', '\${TEST_OPTIONS}') predefinedProp('DEVICE_TYPE', '\${DEVICE_TYPE}') predefinedProp('REPO_URL', '\${REPO_URL}') predefinedProp('EMAIL_RECIPIENTS', '\${EMAIL_RECIPIENTS}') predefinedProp('SEND_EMAIL', '\${SEND_RESULTS}') predefinedProp('DESCRIPTION', '\${DESCRIPTION}\ #Triggered by pmemkv matrixJob #\${BUILD_NUMBER}\ -> \${JENKINS_URL}view/all/job/pmemkv_linux_matrix/\${BUILD_NUMBER}') } block { buildStepFailure('FAILURE') failure('FAILURE') unstable('UNSTABLE') } } } } }""" } } pmemkv-1.5.0/utils/jenkins/README.md000066400000000000000000000160671410000423300170710ustar00rootroot00000000000000# Jenkins Configuration ## Overview Jenkins is a self contained, open sourced automation server which can be used to automate tasks related to building and testing. Its functionality can be expanded by installing plugins. Jenkins main function is to launch jobs. Jobs are scripts that cover managing repositories, building and testing. Jenkins pipeline is a script that is launched by Jenkins job. Each pipeline consists of build stages that follow one another, for example: download repository, build, test, etc. Each stage consists of steps that are actual commands ran on an actual device under test - which which is called: Node. ## Requirements To run tests it is recommended to install these plugins first: - Blue Ocean - Branch API - Command Agent Launcher - Configuration as Code - Dashboard View - Email Extension - External Monitor Job Type - Folders - Git - Git client - GitLab - Job DSL - JUnit - LDAP - Matrix Authorization Strategy - Matrix Combinations - Multijob - Oracle Java SE Development Kit Installer - OWASP Markup Formatter - PAM Authentication - Pipeline Utility Steps - Pipeline: Build Step - Pipeline: Multibranch - Pipeline: REST API - Pipeline: Stage View - SSH Agent - SSH Build Agents - Timestamper ## Managing Jenkins ### Overivew Jenkins can be configured and managed "as a code" - it means that actual jobs and pipelines can be downloaded from remote repository. But first it is needed to add a job that downloads jobs definitions from remote repository and configures Jenkins. Instructions below allows to manually trigger creating/updating jobs, but it is possible to trigger this job automatically with each push to target repository. ### Add master executor Before running any job, it is needed to add an executor which will run Jenkins job. First, it is useful to add executor on master node - which is a server containing Jenkins instance. This executor will lunch job and then pipeline will be executed on the node specified by the "LABEL" field. To add master executor: - Select: "Manage Jenkins". - Select: "Manage Nodes and Clouds". - Select: "master" node. - Select: "Configure". - In the field "# of executors" type "4". - Select: "Save". ### Add job generating jobs To add a Job that generates jobs from Jenkinsfile: - Select: "New item". - Enter jobs name, e.g. "job_creator". - Select: "Pipeline". - Click OK. - In tab "General", select "GitHUB Project", enter project url: "https://github.com/pmem/pmemkv". - In "Pipeline" tab, set fields as below: - Definition: Pipeline script from SCM - SCM: Git - Repositories: - Repository URL: Enter github repository with jenkins job, e.g. "https://github.com/pmem/pmemkv". - Credentials: none - Branches to build: - Branch Specifier (blank for 'any'): master (or any other branch containing jenkinsfile). - Script Path: Enter path with the Jenkinsfile, relative to the root folder of the repository: "utils/jenkins/Jenkinsfile" - Lightweight checkout: yes 7. Save. 8. Enter the new created job from main dashboard. 9. Click on the "Build Now". ### In-process Script Approval By default, Jenkins will prevent to run new groovy scrips which will cause our job to fail. After each fail caused by an unapproved script, it is needed to accept this script. Unfortunately, this will be necessary to repeat several times, by launching this job repeatedly (only after creating this job, for the first time). To approve scripts: - Select: "Manage Jenkins". - Select: "In-process Script Approval". - Click "Approve". ### Test nodes To run Jenkins jobs, it will be needed to add additional Nodes (beside of master Node) which are servers prepared to run tests. Each Node is required to have: - Installed Java Runtime Environment. - Installed and running SSH server, open ssh port (22). - Added user that Jenkins can log on, with appropriate credentials needed to run tests, e.g. "test-user". #### Adding test nodes in this case we will be using server with Fedora31 installed and user "test-user" created. - Select: "Manage Jenkins". - Select: "Manage Nodes and Clouds". - Select: "New Node". - Type name in the "Node name" field, "Server_001(fedora31)". - Select "Permanent Agent" (after creating first node, it is possible to copy existing configuration by selecting "Copy Existing Node"). - Click "OK". - In the field "# of executors" type "1". - In the field "Remote root directory" type directory that Jenkins user has credentials to access to. In our case: /home/test-user - In the field "Labels" type an actual OS installed on server - in our case type "fedora fedora31" NOTE: There can be multiple labels assigned to server. - In the field "Usage" select "Use this node as much as possible". - In the field "Launch method" select "Launch agent agents via SSH". - In the field "Host" type IP address of the server, in format x.x.x.x - In the field "Credentials" click "Add" and then "Jenkins" to create new credentials: - In the field "Domain" select "Global credentials (unrestricted)". - In the field "Kind" select "Username with password". - In the field "Scope" select "System (Jenkins and nodes only)". - In the field "Username" type username - in our case: "test-user". - In the field "Password" enter password. - In the field "ID" enter username - in our case "test-user". - Click "Add" - In the field "Credentials" select newly created credentials - in our case: "test-user". - In the field "Host Key Verification Strategy" select Manually trusted key Verification strategy. - In the field "Availability" select "Keep this agent online as much as possible. - Click "Save" ### Job overview Jenkins jobs can be accessed from main dashboard. To select job click on the name of that job. To run job, enter "Build with Parameters". To access finished job, click on the Build name in the "Build History" section or in the "Stage view" section. In the build view "Build Artifacts" can be accessed, containing "console.log". NOTE: console logs are available also by clicking on "Console Output" or "View as plain text", which is useful when pipeline was unable to generate logs or job failed from script errors or Jenkins related errors, e.g. unapproved scripts. ### Running a Job Enter the Job view and click "Build with Parameters". Some build configuration can be made. To run job, click "Build". #### pmemkv_linux **LABEL** - Name of the node or group to run job on, for example: rhel8_2, openSUSE15_2, fedora32, ubuntu20_04, ubuntu19.10, debian10, etc. Make sure that node with this label exist and its online. **BRANCH** - Pmemkv repository branch. **TEST_TYPE** - PMEMKV test type to run: normal, building, compatibility. **DEVICE_TYPE** - Selects tested device type. If "NONE" - test will run on HDD. **REPO_URL** - Git repository address. **COVERAGE** - Enable or disable code coverage. **EMAIL_RECIPIENTS** - Recipients of the e-mail with job status and sent after execution. **SEND_RESULTS** - Enable or disable email reporting. #### pmemkv_linux_matrix This job will run multiple pmemkv_linux jobs. Instead LABEL and TEST_TYPE there is a combinations matrix. Each check will run job with specified TEST_TYPE on specified OS. pmemkv-1.5.0/utils/jenkins/lib/000077500000000000000000000000001410000423300163465ustar00rootroot00000000000000pmemkv-1.5.0/utils/jenkins/lib/api.groovy000066400000000000000000000365041410000423300203760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2020, Intel Corporation */ /* Jenkinsfile - scripts to create pmemkv and pmemkv_matrix jobs - to run with initial jenkins job. */ /* common functions and variables. */ import java.util.regex.* /* declarations of common paths and filenames: */ /* do not change to "${...}" - it's required to use ' due to expansion on OS's shell */ WORKSPACE_DIR = '${WORKSPACE}' OUTPUT_DIR_NAME = 'output' RESULTS_DIR_NAME = 'results' OUTPUT_DIR = "${WORKSPACE_DIR}/${OUTPUT_DIR_NAME}" RESULTS_DIR = "${WORKSPACE_DIR}/${RESULTS_DIR_NAME}" SCRIPTS_DIR = "${WORKSPACE_DIR}/jenkins_files/utils/jenkins/scripts" LOG_FILENAME = 'console.log' LOG_FILE = "${OUTPUT_DIR}/${LOG_FILENAME}" SYSTEM_INFO_FILENAME = 'system_info.txt' SYSTEM_INFO_FILEPATH = "${RESULTS_DIR}/${SYSTEM_INFO_FILENAME}" COMMON_FILE = "${SCRIPTS_DIR}/common.sh" /* interpolation function for log absolute filepath */ def abs_logfile_path() { return "${WORKSPACE}/${OUTPUT_DIR_NAME}/${LOG_FILENAME}" } /* low-level functions for basic interaction with OS on the DUT: */ /* Required to use top-level function inside class method */ TestsResult.metaClass.get_work_week_of_build_start = { get_work_week_of_build_start() } /** * Runs a script in bash and logs its output. * * @param script_text Bash script's content. No need for shebang, pass only commands to execute. * @param log Path to the file where script's output will be redirected. If empty string, then no output redirection will be used. Default set to LOG_FILE. * @param import_common_file If set to true the 'common.sh' script will be included (default: false). * @param error_on_non_zero_rc If set to true function will raise error when command returns non-zero return code (default: true). * @return object with 'output' field, containing command's output, and 'status' with its returned code. */ def run_bash_script(script_text, log_file = LOG_FILE, import_common_file = false, error_on_non_zero_rc = true) { /* first, prepare optional portions of the script: redirect all script's output to the files: 1. Separate log file for this current command exclusively, with random name. 2. If requested - append to given log file. */ def current_script_output_file = "command_" + generate_random_string(20) + ".log" def redirect_script = """ # ensure that this command output file is empty: echo "" > ${current_script_output_file} # ensure that log_file exists before using `tee -a`: touch ${log_file} ${current_script_output_file} # redirect stdout to the named pipe: exec > >(tee -a ${log_file} ${current_script_output_file}) # merge stderr stream with stdout: exec 2>&1 """ /* import things defined in common.sh file if not set otherwise: */ def source_script = (import_common_file == false) ? "" : "source ${COMMON_FILE}" def full_script_body = """#!/usr/bin/env bash set -o pipefail ${redirect_script} ${source_script} # now do whatever user wanted to: ${script_text} """ /* second, do the actual call: */ def returned_status = sh(script: full_script_body, returnStatus: true) /* third, capture script output from file: */ def script_output = readFile current_script_output_file /* delete the log file: */ sh "rm -f ${current_script_output_file}" /* capturing status is also disabling default behavior of `sh` - error when exit status != 0 */ /* here restore that behavior if not requested otherwise: */ if (error_on_non_zero_rc && returned_status != 0) { error("script returned exit code ${returned_status}") } def retval = [:] retval.output = script_output.trim() retval.status = returned_status return retval } /** * Runs a script in bash and logs its output. Includes sourced 'common.sh'. * @param script_text Bash script's content. No need for shebang, pass only commands to execute. * @param log Path to the file where script's output will be redirected. If empty string, then no output redirection will be used. Default set to LOG_FILE. * @param error_on_non_zero_rc If set to true it will raise error when command returns non-zero return code (Default: true). * @return object with 'output' field, containing command's output, and 'status' with its returned code. */ def run_bash_script_with_common(script_text, log = LOG_FILE, error_on_non_zero_rc = true) { return run_bash_script(script_text, log, true, error_on_non_zero_rc) } /** * Runs CMake script, with parameters and extra env vars, if needed. * @param path A path pointing to directory with CMakeLists.txt file. * @param parameters Additional parameters for CMake configuration. * @param additional_env_vars Set additional env vars. */ def run_cmake(path, parameters = "", additional_env_vars = "") { if (additional_env_vars != "") { additional_env_vars = "export ${additional_env_vars} &&" } run_bash_script("${additional_env_vars} cmake ${path} ${parameters}") } /** * Runs 'make' (on all available cores), with parameters and extra env vars, if needed * @param parameters Additional parameters for make configuration. * @param additional_env_vars Set additional env vars. */ def run_make(parameters = "", additional_env_vars = "") { if (additional_env_vars != "") { additional_env_vars = "export ${additional_env_vars} &&" } run_bash_script("${additional_env_vars} make -j\$(nproc) ${parameters}") } /** * Prints provided text to output log. * @param text String to print in bash. */ def run_echo(text) { run_bash_script("echo \"${text}\"") } /** * Prints given text as a header in Jenkins log and bash's output. * @param text String to print in header. */ def echo_header(text) { def header = "*********************************** ${text} ***********************************" /* echo to Jenkins log: */ echo header /* echo to console output: */ run_echo(header) } /** * Generate random alphanumeric string. * @param length Number of characters to generate. */ def generate_random_string(length) { String chars = (('a'..'z') + ('A'..'Z') + ('0'..'9')).join('') Random rnd = new Random() def random_string = "" for(i = 0; i < length; i++) { random_string = random_string + chars.charAt(rnd.nextInt(chars.length())) } return random_string } /* higher-level functions for dealing with parts of pipelines' stages: */ /** * Download repository with git. * @param repo_url URL to requested repository. * @param branch Specified branch to checkout. * @param target_directory Directory to which repository will be cloned. If not specified, it will be cloned into current working directory. */ def clone_repository(repo_url, branch, target_directory = '') { def specified_target_dir = (target_directory == '') ? false : true /* If target dir is not specified, then we will create temporary dir and then copy contents to current dir. */ /* The reason is: checkout will remove all files from current dir if it's not empty. */ if(!specified_target_dir) { target_directory = "repository_target_temp_${generate_random_string(8)}" } checkout([$class: 'GitSCM', branches: [[name: "${branch}"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: target_directory]], submoduleCfg: [], userRemoteConfigs: [[url: repo_url]]]) echo_header("Git log") run_bash_script("cd ${target_directory} && git log --oneline -n 5") if(!specified_target_dir) { run_bash_script(""" # move all files (including hidden ones) from target_directory: mv -f ${target_directory}/{.[!.],}* ./ # delete target directory: rmdir ${target_directory} """) } } /** * Set a moose as a welcome message after logging to the DUT, which will warn about running execution. */ def set_jenkins_warning_on_dut() { run_bash_script_with_common("set_warning_message ${info_addr}") } /** * Restore default welcome message after logging to the DUT which means that Jenkins' execution is done. */ def unset_jenkins_warning_on_dut() { run_bash_script_with_common("disable_warning_message") } /** * Print OS and BRANCH params of current job to Jenkins. Call script which collects system info and save it to logs. */ def system_info() { echo_header("system info") dir(RESULTS_DIR_NAME) { writeFile file: SYSTEM_INFO_FILENAME, text: '' } print "OS: ${params.LABEL}" print "Branch: ${params.BRANCH}" run_bash_script_with_common(""" system_info 2>&1 | tee -a ${SYSTEM_INFO_FILEPATH} """) } /** * Enumeration for distributions. */ enum DistroName { UNKNOWN(''), DEBIAN('Debian GNU/Linux'), CENTOS('CentOS Linux'), OPENSUSE('openSUSE Leap'), FEDORA('Fedora'), UBUNTU('Ubuntu'), RHEL('Red Hat Enterprise Linux'), RHELS('Red Hat Enterprise Linux Server') /** * Constructor for assigning proper values to enumerators' properties. * @param name Name of the distribution. */ DistroName(String distro_name) { this.distro_name = distro_name } def to_string() { return this.distro_name } static def from_string(string_distro_name) { /* lookup in all enum labels and check if given string matches: */ for (DistroName distro_name : DistroName.values()) { if (distro_name.to_string() == string_distro_name) { return distro_name } } return DistroName.UNKNOWN } private final String distro_name } /* "export" this enum type; */ /* below is necessary in order to closure work properly with enum after loading the file in the pipeline: */ this.DistroName = DistroName /** * Archive artifacts from output directory. */ def archive_output() { archiveArtifacts artifacts: "${OUTPUT_DIR_NAME}/*.*" } /** * Archive artifacts from output and results directory. */ def archive_results_and_output() { archive_output() archiveArtifacts artifacts: "${RESULTS_DIR_NAME}/*.*" } /** * Write 'result' file (success, fail) and archive it. * @param result String representing result (could be e.g. 'success' or 'fail'). */ def write_result_and_archive(result) { dir(RESULTS_DIR_NAME) { writeFile file: "${result}.txt", text: result } archiveArtifacts artifacts: "${RESULTS_DIR_NAME}/*.*" } /** * Write file with name OS_BRANCH_REPO.txt to results dir. */ def write_os_branch_repo(os, branch = null, repo = null) { def branch_chunk = (branch == null) ? "" : "_${branch}" def repo_chunk = (repo == null) ? "" : "_${repo}" def filename = "${os}${branch_chunk}${repo_chunk}.txt" dir(RESULTS_DIR_NAME) { writeFile file: filename, text: '' } } /** * Get week number of date when current build has started. * Assume that the start of the week is on Monday. */ def get_work_week_of_build_start() { /* Groovy sandbox does not allow to change properties of locale, default start of the week is on Sunday, so we need to make a trick: shift the starting date by one day ahead - this will give us in the result proper week number with starting on Monday. roovy sandbox does not allow creation of Date objects with constructor different than Unix milliseconds epoch, so we cannot count in days but only in milliseconds (24 hours / 1 day) * (60 minutes / 1 hour) * (60 seconds / 1 minute) * (1000 milliseconds / 1 second): */ def one_day_in_ms = 24 * 60 * 60 * 1000 /* get build start date object: */ def start_date = new Date(currentBuild.startTimeInMillis + one_day_in_ms) /* get week number and cast it to integer: */ return Integer.valueOf(start_date.format("w")) } class TestsResult { Integer all = 0 Integer passed = 0 Integer failed = 0 Integer skipped = 0 def currentBuild, env TestsResult (results = [], def currentBuild, def env) { results.each { if (it != null) { this.all += it.getTotalCount() this.passed += it.getPassCount() this.failed += it.getFailCount() this.skipped += it.getSkipCount() } } this.currentBuild = currentBuild this.env = env } def get_html_table_data_row() { def urls = "blue, blue tests, tests, build, console, system_info" return """ ${currentBuild.projectName} ${currentBuild.displayName} ww${get_work_week_of_build_start()} ${new Date(currentBuild.startTimeInMillis).format("dd.MM.yyyy HH:mm")} ${currentBuild.durationString} ${currentBuild.currentResult} ${urls} ${this.all} ${this.passed} ${this.skipped} ${this.failed} """ } def get_html_table_heading_row() { return """ Pipeline Build Starting time Duration Pipeline status URLs All tests Passed Skipped Failed """ } def get_html_table() { return """ ${this.get_html_table_heading_row()} ${this.get_html_table_data_row()}
""" } } /** * Send summary with test results via email. * @param results List of objects returned by `junit` call. * @param recipients Addressee of the mail. */ def send_test_summary_via_mail(results = [], recipients = params.EMAIL_RECIPIENTS) { def message_title = "[Jenkins] Report ${currentBuild.projectName} ${currentBuild.displayName}, ww ${get_work_week_of_build_start()}" def tests_results = new TestsResult(results, currentBuild, env) def message_body = """ ${tests_results.get_html_table()}

---
generated automatically by Jenkins

""" mail ( to: recipients, subject: message_title, body: message_body, mimeType: "text/html" ) } def check_os() { distro = sh(script: """#!/usr/bin/env bash source ${libs.api.COMMON_FILE} check_distro """, returnStdout: true).trim() return DistroName.from_string(distro) } /** * Apply regular expression to a text. * @param text Text to apply regex to - input for a regex engine. * @param pattern Regex pattern to be searched by a regex engine. * @param groups List of expected group names to be extracted. * @param pattern_flags Flags for regex engine. Default: MULTILINE | COMMENTS * @return list of dictionaries with all matched groups * Example: * def input_string = "changed: 10.91.28.117\nok: 0.91.28.119" * def pattern = $/ ^(?\w+):\s(?[\w.]+)$ /$ * def groups = ["status", "ip"] * def result = apply_regex(input_string, pattern, groups) * // result will contain list of matches: [[status:changed, ip:10.91.28.117], [status:ok, ip:0.91.28.119]] */ @NonCPS def apply_regex(text, pattern, groups, pattern_flags = Pattern.COMMENTS | Pattern.MULTILINE) { Matcher regex_matcher = Pattern.compile(pattern, pattern_flags).matcher(text); def found_matches = [] while (regex_matcher.find()) { def current_match = [:] for (current_group in groups) { current_match[current_group] = regex_matcher.group(current_group) } found_matches.add(current_match) } return found_matches } /* below is necessary in order to closure work properly after loading the file in the pipeline: */ return this pmemkv-1.5.0/utils/jenkins/lib/snapshots.groovy000066400000000000000000000110221410000423300216330ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ /* This file contains code for pipelines using restore snaphosts. */ /** * Function will run timeshift app on a remote server, using SSH agent plugin. * @param ssh_credentials Specified ssh credentials. Must be set in Jenkins first. * @param user_name User that ssh will try to log on. * @param ssh_address IP address of remote machine. * @param snapshot_name Specified name of a Timeshift snapshot. */ def restore_snapshot(ssh_credentials, user_name, ssh_address, snapshot_name) { /* Expecting to lose connection, when server is rebooted. printf simulates user input necessary to use Timeshift. */ sshagent (credentials: [ssh_credentials]) { libs.api.run_bash_script(""" ssh -o ServerAliveInterval=2 -o ServerAliveCountMax=2 -l ${user_name} ${ssh_address}\\ "printf '\ny\n0\ny\n' | sudo timeshift --restore --snapshot ${snapshot_name}" || true """) } } /** * Function will test and wait for connection to remote server. * @param ssh_credentials Specified ssh credentials. Must be set in Jenkins first. * @param user_name User that ssh will try to log on. * @param ssh_address IP address of remote machine. * @param no_of_tries Number of reconnect tries. * @param con_timeout value of ssh ConnectTimeout parameter. * @param con_attempts value of ssh ConnectionAttempts parameter. */ def wait_for_reconnect(ssh_credentials, user_name, ssh_address, no_of_tries = 200, con_timeout = 1, con_attempts = 1) { sshagent (credentials: [ssh_credentials]) { libs.api.run_bash_script(""" function wait_for_connection() { ssh -o ConnectTimeout=${con_timeout} -o ConnectionAttempts=${con_attempts}\\ -o StrictHostKeyChecking=no -l ${user_name} ${ssh_address} 'true' echo \$? } for (( i=${no_of_tries}; i>0; i-- )) do echo "trying to reach ${ssh_address}. \$i attempts left" if [ "\$(wait_for_connection)" -eq 0 ]; then break elif [ "\$i" -eq "1" ]; then echo "ERROR: Could not reach ${ssh_address}" exit 1 fi sleep 1 done sleep 10 """) } } /** * Reconnect and bring online specified node defined in Jenkins. * @param node_name node name as defined in Jenkins. */ def reconnect_node(node_name) { for (jenkins_node in jenkins.model.Jenkins.instance.slaves) { if (jenkins_node.name == node_name) { println('Bringing node online.') jenkins_node.getComputer().connect(true) jenkins_node.getComputer().setTemporarilyOffline(false) break; } } } /** * Disconnect and set offline specified node defined in Jenkins. * @param node_name node name as defined in Jenkins. */ def diconnect_node(node_name) { for (jenkins_node in jenkins.model.Jenkins.instance.slaves) { if (jenkins_node.name == node_name) { println('Disconnecting node') jenkins_node.getComputer().disconnect(new hudson.slaves.OfflineCause.ByCLI("Disconnected by Jenkins")) } } } /** * Returns Jenkins node IP address. * @param node_name node name as defined in Jenkins. * @return string with Jenkins node IP address. */ def get_node_address(node_name) { for (jenkins_node in jenkins.model.Jenkins.instance.slaves) { if (jenkins_node.name == node_name) { return jenkins_node.getLauncher().getHost() } } } /** * Returns Timeshift snapshot name from the server. * @param snapshot_option Option on which to chose proper Timeshift snapshot. SKIP: skip selecting snapshot name JENKINS_LATEST: A snapshot with description 'Jenkins_backup_latest' will be selected. LATEST: Latest snapshot on the target server will be selected. CHOOSE: A specified snapshot name is chosen, passed in the 'snapshot_option_name'. * @param snapshot_option_name If 'CHOOSE' option is selected then the string in the snapshot_option_name will be chosen as snapshot name. * @return String Snapshot name. */ def get_snapshot_name(snapshot_option, snapshot_option_name='' ) { def snapshot_name = '' switch(snapshot_option) { case 'SKIP': break case 'JENKINS_LATEST': snapshot_name = libs.api.run_bash_script(""" res=\$(sudo timeshift --list-snapshots | grep Jenkins_backup_latest) echo \$res | awk '{print \$3}' | head -n 1 """).output break case 'LATEST': snapshot_name = libs.api.run_bash_script(""" res=\$(sudo timeshift --list-snapshots | tail -n 2) echo \$res | awk '{print \$3}' """).output break case 'CHOOSE': snapshot_name = snapshot_option_name break default: throw new java.lang.UnsupportedOperationException("'${snapshot_option}' is not a valid option.") } return snapshot_name } return this pmemkv-1.5.0/utils/jenkins/pmemkv_linux.jenkins000066400000000000000000000144371410000423300217120ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2019-2020, Intel Corporation */ /* pmemkv.jenkins - pipeline for pmemkv jenkins job. */ /* declare a map object for holding loaded libraries: */ libs = [:] currentBuild.displayName = "#$currentBuild.id $LABEL: $BRANCH - $TEST_TYPE, coverage: $COVERAGE" currentBuild.description = "$DESCRIPTION" PMEMKV_DIR='pmemkv' /* Send report summary via email. */ def send_mail(attachments, msg) { def recipients = params.EMAIL_RECIPIENTS def message_title = "[Jenkins/pmemkv] Report ${currentBuild.projectName} ${currentBuild.displayName}" def message_body = """

---
Auto-generated by Jenkins

---
${params.REPO_URL} BRANCH: ${params.BRANCH}


Build link

Description: ${params.DESCRIPTION}

""" message_body += msg emailext ( to: recipients, subject: message_title, body: message_body, mimeType: "text/html", attachmentsPattern: attachments ) } pipeline { /* put timestamps in output log. */ options { timestamps () } /* execute this pipeline job on node in "LABEL" group in Jenkins. */ agent { label params.LABEL } /* each stage represents pipeline step. */ stages { /* preparation stage: clean workspace, create output directories, log file, clone repository containing Jenkins pipelines, load libraries, set warning on DUT, etc. */ stage('Prepare') { steps { print "Deleting ${WORKSPACE} directory" deleteDir() print "Creating ${WORKSPACE}/output directory" dir('output') { writeFile file: 'console.log', text: '' } print "Checkout repository containing Jenkins pipelines" checkout([$class: 'GitSCM', branches: [[name: jenkins_files_branch]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'jenkins_files']], submoduleCfg: [], userRemoteConfigs: [[url: jenkins_files_repo]]]) print "Loading pipeline libraries" script { libs.api = load api_lib libs.api.set_jenkins_warning_on_dut() } } } /* gather system info. */ stage('System-info'){ steps { script { libs.api.system_info() } } } /* clone pmemkv repository to be tested */ stage('Repo checkout') { steps { script { def PMEMKV_PATH="${WORKSPACE_DIR}/${PMEMKV_DIR}" libs.api.clone_repository(params.REPO_URL, params.BRANCH, PMEMKV_DIR) } } } stage('Run test') { steps { script { warnError('Build unstable.') { libs.api.echo_header("Running tests") def COV_PARAM="" def TEST_DIR if (params.COVERAGE == 'yes') { COV_PARAM="export COVERAGE=1" } def PMEMKV_PATH="${WORKSPACE}/${PMEMKV_DIR}" switch (params.DEVICE_TYPE) { case 'DAX': libs.api.run_bash_script(""" ${SCRIPTS_DIR}/createNamespace.sh -d --size=100G """) TEST_DIR = "/dev/" + libs.api.run_bash_script(""" sudo ndctl list -M -N | jq -r '.[] | select(.mode=="devdax").chardev' | head -n 1 """).output break case 'PMEM': libs.api.run_bash_script(""" ${SCRIPTS_DIR}/createNamespace.sh -p --size=100G """) TEST_DIR = "/mnt/pmem0" break case 'NONE': TEST_DIR = "/dev/shm" break } def ENV_VAL="export DEFAULT_TEST_DIR=${TEST_DIR}; export WORKDIR=${WORKSPACE_DIR}/${PMEMKV_DIR}; export SCRIPTSDIR=\$WORKDIR/utils/docker; export TESTS_LONG=ON; export TERM=xterm-256color; export TEST_TIMEOUT=3600; export TESTS_USE_FORCED_PMEM=0; ${COV_PARAM}" libs.api.echo_header("Running test type: ${params.TEST_TYPE}") dir("${PMEMKV_PATH}/utils/docker") { switch (params.TEST_TYPE) { case 'normal': libs.api.echo_header("Test options: ${params.TEST_OPTIONS}") params.TEST_OPTIONS.split().each { build -> stage("${build}") { catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { libs.api.run_bash_script(""" ${ENV_VAL} ./run-build.sh ${build} """) } } } break; case 'building': libs.api.run_bash_script(""" ${ENV_VAL} ./run-test-building.sh """) break; case 'compatibility': libs.api.run_bash_script(""" ${ENV_VAL} ./run-compatibility.sh """) break; default: throw new java.lang.UnsupportedOperationException("TEST_TYPE value '${params.TEST_TYPE}' is not supported. Select: normal, building or compatibility."); } } libs.api.echo_header("Finished running tests.") } } } } } /* generate post-build artifacts write "fail" or "success" in result.txt file archive results */ post { /* 'always' declarations must be declared first. */ always { script { libs.api.unset_jenkins_warning_on_dut() libs.api.write_os_branch_repo(params.LABEL, params.BRANCH, params.REPO_URL) libs.api.archive_output() /* Make sure to uninstall pmemkv from the DUT. */ def PMEMKV_PATH="${WORKSPACE_DIR}/${PMEMKV_DIR}" libs.api.run_bash_script(""" cd ${PMEMKV_PATH}/build || true sudo make uninstall || true sudo rm /usr/lib64/pkgconfig/libpmemkv.pc || true sudo rm /usr/lib/x86_64-linux-gnu/pkgconfig/libpmemkv.pc || true """) } } success { script { libs.api.write_result_and_archive('success') if (params.SEND_RESULTS) { def build_info = "Build" + " #$currentBuild.id " + "succeeded" send_mail('', build_info) } } } failure { script { libs.api.write_result_and_archive('fail') if (params.SEND_RESULTS) { def build_info = "Build" + " #$currentBuild.id " + "failed" send_mail('', build_info) } } } unstable { script { libs.api.write_result_and_archive('fail') if (params.SEND_RESULTS) { def build_info = "Build" + " #$currentBuild.id " + "failed" send_mail('', build_info) } } } } } pmemkv-1.5.0/utils/jenkins/restore_snapshot.jenkins000066400000000000000000000112261410000423300225670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* Copyright 2021, Intel Corporation */ /* restore_snapshot.jenkins - pipeline for restoring Timeshift snapshots. Triggers downstream job if succeeded. */ /* declare a map object for holding loaded libraries: */ libs = [:] if (params.SNAP_OPT == 'CHOOSE') { SNAP_OPT_DISPLAY = "$SNAP_OPT: $SNAP_OPT_NAME" } else { SNAP_OPT_DISPLAY = "$SNAP_OPT" } currentBuild.displayName = "#$currentBuild.id $LABEL, Option: $SNAP_OPT_DISPLAY, Downstream: $DOWNSTREAM_JOB" /* ####### Pipeline restoring Timeshift snapshots. ####### Requirements: - Linux OS with installed and ready to use Timeshift. - Installed SSH agent plugin in Jenkins. - Imported SSH private key into Jenkins' credential manager. (Make sure that ssh private key matches in all servers on which this pipeline will be used) This pipeline script doesn't create snapshot. Snapshots must be present before using this pipeline. There are four options to use in this pipeline, depending of used "SNAP_OPT" option: - SKIP: skip restore stages and go to actual tasks. - JENKINS_LATEST: use Timeshift snapshot that has "Jenkins_backup_latest" in its description field. Useful when many snapshots are present on the target server and it has specified a restore point for CI. - LATEST: Use most recently created snapshot found on the server. - CHOOSE: Chose specified name of the snapshot. Insert name in the "SNAP_OPT_NAME" field. */ def targetName def targetUser def targetAdress def snapshotName /* Pass all parameters to downtream job.*/ def pass_params = params.collect{ string(name: it.key, value: String.valueOf(it.value)) } pipeline { /* put timestamps in output log. */ options { timestamps () } /* execute this pipeline job on the Jenkins master executor. */ agent { label 'master' } /* each stage represents pipeline step. */ stages { stage('Prepare Job') { steps { print "Deleting ${WORKSPACE} directory" deleteDir() print "Creating ${WORKSPACE}/output directory" dir('output') { writeFile file: 'console.log', text: '' } print "Checkout repository containing Jenkins pipelines" checkout([$class: 'GitSCM', branches: [[name: jenkins_files_branch]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'jenkins_files']], submoduleCfg: [], userRemoteConfigs: [[url: jenkins_files_repo]]]) script { libs.api = load api_lib libs.snapshots = load lib_path + "/snapshots.groovy" } } } stage('Gather information about target') { /* Switch to target node. */ agent { label params.LABEL } steps { script { targetName = env.NODE_NAME targetUser = libs.api.run_bash_script(""" whoami """).output targetAdress = libs.snapshots.get_node_address(targetName) snapshotName = libs.snapshots.get_snapshot_name(params.SNAP_OPT, params.SNAP_OPT_NAME) } } } stage('Disconnect target') { /* Skip stage when restore is not needed. */ when { expression { params.SNAP_OPT != 'SKIP' } } /* Switch to target node. */ steps { node(targetName) { script { libs.snapshots.diconnect_node(targetName) } } } } stage('Restore snapshot on target') { /* Skip stage when restore is not needed. */ when { expression { params.SNAP_OPT != 'SKIP' } } steps { script { libs.snapshots.restore_snapshot(params.SSH_CRED, targetUser, targetAdress, snapshotName) libs.snapshots.wait_for_reconnect(params.SSH_CRED, targetUser, targetAdress) libs.snapshots.reconnect_node(targetName) } } } } /* generate post-build artifacts write "fail" or "success" in result.txt file archive results */ post { /* 'always' declarations must be declared first. */ always { script { libs.api.write_os_branch_repo(params.LABEL, params.BRANCH, params.REPO_URL) libs.api.archive_output() } } success { script { libs.api.write_result_and_archive('success') pass_params.add(string(name: 'LABEL', value: targetName)) jobResult = build job: params.DOWNSTREAM_JOB, parameters: pass_params, wait: true, propagate: false /* Add information about downstream job to builds name and description.*/ currentBuild.displayName = "#$currentBuild.id $LABEL, Option: ${SNAP_OPT_DISPLAY}, Downstream: ${DOWNSTREAM_JOB}#${jobResult.number}" currentBuild.description = "JOB: ${DOWNSTREAM_JOB}#${jobResult.number} STATUS: ${jobResult.getResult()}" } } failure { script { libs.api.write_result_and_archive('fail') } } unstable { script { libs.api.write_result_and_archive('fail') } } } } pmemkv-1.5.0/utils/jenkins/scripts/000077500000000000000000000000001410000423300172675ustar00rootroot00000000000000pmemkv-1.5.0/utils/jenkins/scripts/common.sh000066400000000000000000000053701410000423300211200ustar00rootroot00000000000000#!/usr/bin/env bash # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019-2020, Intel Corporation # common.sh - contains bash functions used in all jenkins pipelines. set -o pipefail scriptdir=$(readlink -f $(dirname ${BASH_SOURCE[0]})) function system_info { echo "********** system-info **********" cat /etc/os-release | grep -oP "PRETTY_NAME=\K.*" uname -r echo "libndctl: $(pkg-config --modversion libndctl || echo 'libndctl not found')" echo "libfabric: $(pkg-config --modversion libfabric || echo 'libfabric not found')" echo "libpmem: $(pkg-config --modversion libpmem || echo 'libpmem not found')" echo "libpmemobj: $(pkg-config --modversion libpmemobj || echo 'libpmemobj not found')" echo "libpmemobj++: $(pkg-config --modversion libpmemobj++ || echo 'libpmemobj++ not found')" echo "memkind: $(pkg-config --modversion memkind || echo 'memkind not found')" echo "TBB : $(pkg-config --modversion TBB || echo 'TBB not found')" echo "valgrind: $(pkg-config --modversion valgrind || echo 'valgrind not found')" echo "**********memory-info**********" sudo ipmctl show -dimm || true sudo ipmctl show -topology || true echo "**********list-existing-namespaces**********" sudo ndctl list -M -N echo "**********installed-packages**********" zypper se --installed-only 2>/dev/null || true apt list --installed 2>/dev/null || true yum list installed 2>/dev/null || true echo "**********/proc/cmdline**********" cat /proc/cmdline echo "**********/proc/modules**********" cat /proc/modules echo "**********/proc/cpuinfo**********" cat /proc/cpuinfo echo "**********/proc/meminfo**********" cat /proc/meminfo eco "**********/proc/swaps**********" cat /proc/swaps echo "**********/proc/version**********" cat /proc/version echo "**********check-updates**********" sudo zypper list-updates 2>/dev/null || true sudo apt-get update 2>/dev/null || true ; apt upgrade --dry-run 2>/dev/null || true sudo dnf check-update 2>/dev/null || true echo "**********list-enviroment**********" env } function set_warning_message { local info_addr=$1 sudo bash -c "cat > /etc/motd <