diff --git a/.github/docker/alpine-3.21.Dockerfile b/.github/docker/alpine-3.21.Dockerfile new file mode 100644 index 000000000..fef981070 --- /dev/null +++ b/.github/docker/alpine-3.21.Dockerfile @@ -0,0 +1,46 @@ +# Copyright (C) 2025 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# +# Dockerfile - a 'recipe' for Docker to build an image of Alpine +# environment for building the Unified Memory Framework project. +# + +# Pull base Alpine image version 3.21 +FROM registry.hub.docker.com/library/alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c + +# Set environment variables +ENV OS=alpine +ENV OS_VER=3.21 + +# Base development packages +ARG BASE_DEPS="\ + bash \ + cmake \ + git \ + g++ \ + make \ + sudo" + +# UMF's dependencies +ARG UMF_DEPS="\ + hwloc-dev" + +# Dependencies for tests +ARG TEST_DEPS="\ + numactl-dev" + +# Update and install required packages +RUN apk update \ + && apk add --no-cache \ + ${BASE_DEPS} \ + ${TEST_DEPS} \ + ${UMF_DEPS} + +# Add a new (non-root) 'test_user' +ENV USER=test_user +RUN adduser -D -G wheel ${USER} +RUN echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + +USER test_user diff --git a/.github/scripts/alpine_build.sh b/.github/scripts/alpine_build.sh new file mode 100755 index 000000000..4bfdb4461 --- /dev/null +++ b/.github/scripts/alpine_build.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (C) 2025 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# alpine_build.sh - Script for building UMF on Alpine image + +set -e + +UMF_BUILD_TYPE=$1 +WORKDIR=$2 + +sudo chown $USER $WORKDIR +cd unified-memory-framework + +cmake -B build -DCMAKE_BUILD_TYPE=$UMF_BUILD_TYPE -DUMF_BUILD_TESTS=ON -DUMF_BUILD_EXAMPLES=ON +cmake --build build diff --git a/.github/workflows/.spellcheck-conf.toml b/.github/workflows/.spellcheck-conf.toml index 288af6a19..bb0f480d6 100644 --- a/.github/workflows/.spellcheck-conf.toml +++ b/.github/workflows/.spellcheck-conf.toml @@ -1,6 +1,6 @@ [default] # Don't correct the following words: -extend-ignore-words-re = ["ASSER", "Tne", "ba", "BA", "PN"] +extend-ignore-words-re = ["ASSER", "Tne", "ba", "BA", "PN", "usm"] [files] # completely exclude those files from consideration: diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index a087d1197..d613cea9c 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ github.ref }} fetch-depth: 0 diff --git a/.github/workflows/detect_changes.yml b/.github/workflows/detect_changes.yml index 4c1c9d4bf..1cb8c39f6 100644 --- a/.github/workflows/detect_changes.yml +++ b/.github/workflows/detect_changes.yml @@ -21,7 +21,7 @@ jobs: changed_files: ${{ steps.changed-files.outputs.all_changed_files }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index aaaae6ddc..f7627ca04 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -79,7 +79,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -126,12 +126,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Restore vcpkg cache - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -243,7 +243,7 @@ jobs: - name: Save vcpkg cache if: steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} @@ -268,12 +268,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Restore vcpkg cache - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -354,7 +354,7 @@ jobs: - name: Save vcpkg cache if: steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} @@ -380,7 +380,7 @@ jobs: run: sudo apt-get install -y libnuma-dev - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -422,7 +422,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -495,7 +495,13 @@ jobs: # Run benchmarks with the latest SYCL (with the latest UMF copied into the SYCL) # to verify the compatibility. + # + # TODO: re-enable this job, when nightly sycl builds are again available; + # the last one available (as of 24.07.2025) is not working properly with + # compute benchmarks. Now, we could only build sycl from sources, or find a + # matching version of compute benchmarks with last nightly package. Benchmarks-sycl: + if: false uses: ./.github/workflows/reusable_benchmarks.yml permissions: contents: write @@ -513,3 +519,28 @@ jobs: SYCL: uses: ./.github/workflows/reusable_sycl.yml + + alpine: + name: Alpine + env: + HOST_WORKDIR: ${{github.workspace}} + WORKDIR: /unified-memory-framework + strategy: + matrix: + build_type: [Debug, Release] + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Build Alpine image + run: | + docker build . -f .github/docker/alpine-3.21.Dockerfile -t umf-alpine-3.21 + + - name: Run UMF build on Alpine image + run: | + docker run --rm -i -v $HOST_WORKDIR:$WORKDIR \ + umf-alpine-3.21 $WORKDIR/.github/scripts/alpine_build.sh ${{matrix.build_type}} $WORKDIR diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml index 52bd73756..cef83c9e8 100644 --- a/.github/workflows/pr_push.yml +++ b/.github/workflows/pr_push.yml @@ -99,6 +99,6 @@ jobs: uses: ./.github/workflows/reusable_compatibility.yml strategy: matrix: - tag: ["v1.0.0"] + tag: ["v1.0.1"] with: tag: ${{matrix.tag}} diff --git a/.github/workflows/reusable_basic.yml b/.github/workflows/reusable_basic.yml index 7980e2939..3c4d63c00 100644 --- a/.github/workflows/reusable_basic.yml +++ b/.github/workflows/reusable_basic.yml @@ -30,7 +30,6 @@ jobs: level_zero_provider: ['ON'] cuda_provider: ['ON'] install_tbb: ['ON'] - disable_hwloc: ['OFF'] link_hwloc_statically: ['OFF'] cmake_ver: ['default'] include: @@ -41,7 +40,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' # check minimum supported cmake version cmake_ver: '3.14.0' @@ -52,7 +50,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' cmake_ver: '3.28.0' - os: ubuntu-24.04 @@ -62,7 +59,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' cmake_ver: 'default' # test level_zero_provider='OFF' and cuda_provider='OFF' @@ -73,7 +69,6 @@ jobs: level_zero_provider: 'OFF' cuda_provider: 'OFF' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' cmake_ver: 'default' # test icx compiler @@ -84,7 +79,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' cmake_ver: 'default' # test lld linker @@ -95,7 +89,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'OFF' llvm_linker: '-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_MODULE_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld"' cmake_ver: 'default' @@ -107,17 +100,6 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'OFF' - disable_hwloc: 'OFF' - link_hwloc_statically: 'OFF' - cmake_ver: 'default' - - os: ubuntu-22.04 - build_type: Debug - compiler: {c: gcc, cxx: g++} - shared_library: 'ON' - level_zero_provider: 'ON' - cuda_provider: 'ON' - install_tbb: 'ON' - disable_hwloc: 'ON' link_hwloc_statically: 'OFF' cmake_ver: 'default' - os: ubuntu-22.04 @@ -127,14 +109,13 @@ jobs: level_zero_provider: 'ON' cuda_provider: 'ON' install_tbb: 'ON' - disable_hwloc: 'OFF' link_hwloc_statically: 'ON' cmake_ver: 'default' - name: Basic (${{matrix.os}}, build_type=${{matrix.build_type}}, compilers=${{matrix.compiler.c}}/${{matrix.compiler.cxx}}, shared_library=${{matrix.shared_library}}, level_zero_provider=${{matrix.level_zero_provider}}, cuda_provider=${{matrix.cuda_provider}}, install_tbb=${{matrix.install_tbb}}, disable_hwloc=${{matrix.disable_hwloc}}, link_hwloc_statically=${{matrix.link_hwloc_statically}}, cmake_ver=${{matrix.cmake_ver}}) + name: Basic (${{matrix.os}}, build_type=${{matrix.build_type}}, compilers=${{matrix.compiler.c}}/${{matrix.compiler.cxx}}, shared_library=${{matrix.shared_library}}, level_zero_provider=${{matrix.level_zero_provider}}, cuda_provider=${{matrix.cuda_provider}}, install_tbb=${{matrix.install_tbb}}, link_hwloc_statically=${{matrix.link_hwloc_statically}}, cmake_ver=${{matrix.cmake_ver}}) steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -146,12 +127,6 @@ jobs: chmod +x cmake-${{matrix.cmake_ver}}-Linux-x86_64.sh echo ${USERPASS} | sudo -Sk ./cmake-${{matrix.cmake_ver}}-Linux-x86_64.sh --skip-license --prefix=/usr/local - - name: Uninstall hwloc - if: matrix.disable_hwloc == 'ON' - run: | - echo ${USERPASS} | sudo -Sk apt-get remove --purge -y '*hwloc*' - echo ${USERPASS} | sudo -Sk apt-get autoremove -y - - name: Uninstall TBB apt package if: matrix.install_tbb == 'OFF' run: | @@ -185,7 +160,6 @@ jobs: -DUMF_DEVELOPER_MODE=ON -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON -DUMF_TESTS_FAIL_ON_SKIP=ON - -DUMF_DISABLE_HWLOC=${{matrix.disable_hwloc}} -DUMF_LINK_HWLOC_STATICALLY=${{matrix.link_hwloc_statically}} ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' && '-DUMF_USE_COVERAGE=ON' || '' }} ${{ matrix.llvm_linker || '' }} @@ -205,7 +179,7 @@ jobs: if: ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' }} working-directory: ${{env.BUILD_DIR}} run: | - export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}}-no_hwloc-${{matrix.disable_hwloc}} + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}} echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME mkdir -p ${{env.COVERAGE_DIR}} @@ -214,7 +188,7 @@ jobs: - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' }} with: - name: ${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}}-no_hwloc-${{matrix.disable_hwloc}} + name: ${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}} path: ${{env.COVERAGE_DIR}} - name: Remove the installation directory @@ -226,7 +200,7 @@ jobs: --build-dir ${{env.BUILD_DIR}} --install-dir ${{env.INSTL_DIR}} --build-type ${{matrix.build_type}} - ${{ matrix.install_tbb == 'ON' && matrix.disable_hwloc != 'ON' && matrix.shared_library == 'ON' && '--proxy' || '' }} + ${{ matrix.install_tbb == 'ON' && matrix.shared_library == 'ON' && '--proxy' || '' }} --umf-version ${{env.UMF_VERSION}} ${{ matrix.shared_library == 'ON' && '--shared-library' || '' }} @@ -281,7 +255,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -312,7 +286,7 @@ jobs: arch: x64 - name: Restore vcpkg cache - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -462,7 +436,7 @@ jobs: - name: Save vcpkg cache if: steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} @@ -477,7 +451,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -519,7 +493,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -560,7 +534,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/reusable_benchmarks.yml b/.github/workflows/reusable_benchmarks.yml index 45434c7d9..512ea35d7 100644 --- a/.github/workflows/reusable_benchmarks.yml +++ b/.github/workflows/reusable_benchmarks.yml @@ -76,7 +76,7 @@ jobs: }) - name: Checkout UMF - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: path: ${{env.UMF_DIR}} fetch-depth: 0 @@ -113,7 +113,7 @@ jobs: run: cmake --build ${{env.BUILD_DIR}} -j $(nproc) - name: Checkout UMF results branch - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: benchmark-results path: results-repo @@ -121,14 +121,14 @@ jobs: # Get scripts for benchmark data visualization (from SYCL repo). # Use specific ref, as the scripts or files' location may change. - name: Checkout benchmark scripts - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: repository: intel/llvm # Note: The same ref is used in docs build (for dashboard generation)! # - # 11.07.2025 + # 30.07.2025 # branch: sycl - ref: b68f49e0a03fb63de5a3e207f0f65247964d337b + ref: 8f54710553800eec05a6fd9717b14f995a22b137 path: sc sparse-checkout: | devops/scripts/benchmarks diff --git a/.github/workflows/reusable_checks.yml b/.github/workflows/reusable_checks.yml index e27807de9..180e56a4f 100644 --- a/.github/workflows/reusable_checks.yml +++ b/.github/workflows/reusable_checks.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -57,7 +57,7 @@ jobs: ./scripts/check_license/check_headers.sh . "Apache-2.0 WITH LLVM-exception" -v - name: Run a spell check - uses: crate-ci/typos@392b78fe18a52790c53f42456e46124f77346842 # v1.34.0 + uses: crate-ci/typos@a4c3e43aea0a9e9b9e6578d2731ebd9a27e8f6cd # v1.35.5 with: config: ./.github/workflows/.spellcheck-conf.toml diff --git a/.github/workflows/reusable_codeql.yml b/.github/workflows/reusable_codeql.yml index af3ec72ab..511ab9843 100644 --- a/.github/workflows/reusable_codeql.yml +++ b/.github/workflows/reusable_codeql.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -41,13 +41,14 @@ jobs: python-version: "3.10" - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: languages: cpp + trap-caching: false - name: "[Win] Restore vcpkg cache" if: matrix.os == 'windows-latest' - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -107,7 +108,7 @@ jobs: run: cmake --build ${{env.BUILD_DIR}} --config Release -j - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 - name: "[Win] Prepare vcpkg cache" if: matrix.os == 'windows-latest' && steps.cache.outputs.cache-hit != 'true' @@ -116,7 +117,7 @@ jobs: - name: "[Win] Save vcpkg cache" if: matrix.os == 'windows-latest' && steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} diff --git a/.github/workflows/reusable_compatibility.yml b/.github/workflows/reusable_compatibility.yml index 98eb223bb..2db924bf2 100644 --- a/.github/workflows/reusable_compatibility.yml +++ b/.github/workflows/reusable_compatibility.yml @@ -9,7 +9,10 @@ on: tag: description: Check backward compatibility with this tag type: string - default: "v1.0.0" + # While we're still compatible with v1.0.0, we implemented a fix in v1.0.1 + # to verify if the split operation is supported (in jemalloc pool). + # Without bumping the tag we'd have to omit some tests. + default: "v1.0.1" permissions: contents: read @@ -26,14 +29,14 @@ jobs: sudo apt-get install -y clang cmake hwloc libhwloc-dev libnuma-dev libtbb-dev - name: Checkout "tag" UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 ref: refs/tags/${{inputs.tag}} path: ${{github.workspace}}/tag_version - name: Checkout latest UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 path: ${{github.workspace}}/latest_version @@ -95,21 +98,12 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build - # Exclude the test_jemalloc_pool test - - # TODO: add fix for that in v1.0.1 - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose -E test_jemalloc_pool - - - name: Run EXCLUDED tests with filters - working-directory: ${{github.workspace}}/tag_version/build - # Exclude the jemallocPoolName test case of the test_jemalloc_pool test - # TODO: add fix for that in v1.0.1 - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ./test/test_jemalloc_pool --gtest_filter="-*jemallocPoolName*" + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no + LD_LIBRARY_PATH: ${{github.workspace}}/latest_version/build/lib/ + run: | + ctest --verbose -E "test_memoryProvider" + test/test_memoryProvider --gtest_filter="-*Trace" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -141,14 +135,14 @@ jobs: steps: - name: Checkout "tag" UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 ref: refs/tags/${{inputs.tag}} path: ${{github.workspace}}/tag_version - name: Restore vcpkg cache - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -174,7 +168,7 @@ jobs: run: vcpkg install --triplet x64-windows - name: Checkout latest UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 path: ${{github.workspace}}/latest_version @@ -231,22 +225,13 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build - # Exclude the test_jemalloc_pool test - - # TODO: add fix for that in v1.0.1 + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no run: | - $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" cp ${{github.workspace}}/latest_version/build/bin/Debug/umf.dll ${{github.workspace}}/tag_version/build/bin/Debug/umf.dll - ctest -C Debug --verbose -E test_jemalloc_pool - - - name: Run EXCLUDED tests with filters - working-directory: ${{github.workspace}}/tag_version/build/ - # Exclude the jemallocPoolName test case of the test_jemalloc_pool test - # TODO: add fix for that in v1.0.1 - run: | - $env:UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" + ctest -C Debug --verbose -E "test_memoryProvider" $env:Path = "${{github.workspace}}/tag_version/build/bin/Debug;${{env.VCPKG_BIN_PATH}};$env:Path" - cp ${{github.workspace}}/latest_version/build/bin/Debug/umf.dll ${{github.workspace}}/tag_version/build/bin/Debug/umf.dll - test/Debug/test_jemalloc_pool.exe --gtest_filter="-*jemallocPoolName*" + test/Debug/test_memoryProvider.exe --gtest_filter="-*Trace" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. @@ -297,7 +282,7 @@ jobs: - name: Save vcpkg cache if: steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} @@ -313,7 +298,7 @@ jobs: steps: - name: Checkout latest UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 path: ${{github.workspace}}/latest_version @@ -345,7 +330,7 @@ jobs: run: cmake --install ${{github.workspace}}/latest_version/build --config Debug - name: Checkout "tag" UMF version - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 ref: refs/tags/${{inputs.tag}} @@ -384,21 +369,12 @@ jobs: - name: Run "tag" UMF tests with latest UMF libs (warnings enabled) working-directory: ${{github.workspace}}/tag_version/build - # Exclude the test_jemalloc_pool test - - # TODO: add fix for that in v1.0.1 - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ctest --verbose -E test_jemalloc_pool - - - name: Run EXCLUDED tests with filters - working-directory: ${{github.workspace}}/tag_version/build - # Exclude the jemallocPoolName test case of the test_jemalloc_pool test - # TODO: add fix for that in v1.0.1 - run: > - UMF_LOG="level:warning;flush:debug;output:stderr;pid:no" - LD_LIBRARY_PATH=${{github.workspace}}/latest_version/build/lib/ - ./test/test_jemalloc_pool --gtest_filter="-*jemallocPoolName*" + env: + UMF_LOG: level:warning;flush:debug;output:stderr;pid:no + LD_LIBRARY_PATH: ${{github.workspace}}/latest_version/build/lib/ + run: | + ctest --verbose -E "test_memoryProvider" + test/test_memoryProvider --gtest_filter="-*Trace" # Browse all folders in the examples directory, build them using the # latest UMF version, and run them, excluding those in the exclude list. diff --git a/.github/workflows/reusable_coverage.yml b/.github/workflows/reusable_coverage.yml index b632b718d..910ed55ed 100644 --- a/.github/workflows/reusable_coverage.yml +++ b/.github/workflows/reusable_coverage.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -32,7 +32,7 @@ jobs: sudo apt-get install -y lcov - name: Download all coverage artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: pattern: exports-coverage-* path: coverage diff --git a/.github/workflows/reusable_dax.yml b/.github/workflows/reusable_dax.yml index b86b4138c..821d20b5a 100644 --- a/.github/workflows/reusable_dax.yml +++ b/.github/workflows/reusable_dax.yml @@ -65,7 +65,7 @@ jobs: rm -f ${{env.UMF_TESTS_FSDAX_PATH}} ${{env.UMF_TESTS_FSDAX_PATH_2}} - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/reusable_dockers_build.yml b/.github/workflows/reusable_dockers_build.yml index a0a84ab0e..9d85ecefa 100644 --- a/.github/workflows/reusable_dockers_build.yml +++ b/.github/workflows/reusable_dockers_build.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -34,7 +34,7 @@ jobs: # Login and push require login/pass to GHCR - omit these steps on forks - name: Login to GitHub Container Registry if: ${{ github.event_name != 'pull_request' && github.repository == 'oneapi-src/unified-memory-framework' }} - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 with: registry: ghcr.io username: bb-ur diff --git a/.github/workflows/reusable_docs_build.yml b/.github/workflows/reusable_docs_build.yml index 2874d1e95..8cb585e3d 100644 --- a/.github/workflows/reusable_docs_build.yml +++ b/.github/workflows/reusable_docs_build.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -41,8 +41,7 @@ jobs: -DUMF_BUILD_LEVEL_ZERO_PROVIDER=OFF \ -DUMF_BUILD_CUDA_PROVIDER=OFF \ -DUMF_BUILD_TESTS=OFF \ - -DUMF_BUILD_EXAMPLES=OFF \ - -DUMF_DISABLE_HWLOC=ON + -DUMF_BUILD_EXAMPLES=OFF cmake --build build --target docs # @@ -51,12 +50,12 @@ jobs: # - name: Checkout benchmark scripts if: ${{ inputs.upload == true }} - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: repository: intel/llvm - # 11.07.2025 + # 30.07.2025 # branch: sycl - ref: b68f49e0a03fb63de5a3e207f0f65247964d337b + ref: 8f54710553800eec05a6fd9717b14f995a22b137 path: sc sparse-checkout: | devops/scripts/benchmarks @@ -79,6 +78,6 @@ jobs: - name: Upload artifact if: ${{ inputs.upload == true }} - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 with: path: build/docs_build/generated/html diff --git a/.github/workflows/reusable_fast.yml b/.github/workflows/reusable_fast.yml index 3a3360468..fd91b876a 100644 --- a/.github/workflows/reusable_fast.yml +++ b/.github/workflows/reusable_fast.yml @@ -43,7 +43,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -104,12 +104,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Restore vcpkg cache - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -185,7 +185,7 @@ jobs: - name: Save vcpkg cache if: steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} diff --git a/.github/workflows/reusable_gpu.yml b/.github/workflows/reusable_gpu.yml index f0d1bcda8..3f2e61e2f 100644 --- a/.github/workflows/reusable_gpu.yml +++ b/.github/workflows/reusable_gpu.yml @@ -65,7 +65,7 @@ jobs: echo "PROCS=$(nproc)" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -75,7 +75,7 @@ jobs: - name: "[Win] Restore vcpkg cache" if: matrix.os == 'Windows' - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 id: cache with: path: vcpkg_pkgs_cache.zip @@ -197,7 +197,7 @@ jobs: - name: "[Win] Save vcpkg cache" if: matrix.os == 'Windows' && steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ${{github.workspace}}/vcpkg_pkgs_cache.zip key: ${{ steps.cache.outputs.cache-primary-key }} diff --git a/.github/workflows/reusable_multi_numa.yml b/.github/workflows/reusable_multi_numa.yml index 63075c6f9..ebb4705c9 100644 --- a/.github/workflows/reusable_multi_numa.yml +++ b/.github/workflows/reusable_multi_numa.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/reusable_proxy_lib.yml b/.github/workflows/reusable_proxy_lib.yml index 5aed20984..3c9067877 100644 --- a/.github/workflows/reusable_proxy_lib.yml +++ b/.github/workflows/reusable_proxy_lib.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/reusable_qemu.yml b/.github/workflows/reusable_qemu.yml index df4125d1a..a8e7740a1 100644 --- a/.github/workflows/reusable_qemu.yml +++ b/.github/workflows/reusable_qemu.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout UMF - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 path: umf diff --git a/.github/workflows/reusable_sanitizers.yml b/.github/workflows/reusable_sanitizers.yml index 0af7828ab..27cd82391 100644 --- a/.github/workflows/reusable_sanitizers.yml +++ b/.github/workflows/reusable_sanitizers.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -91,7 +91,7 @@ jobs: # # steps: # - name: Checkout - # uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + # uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 # with: # fetch-depth: 0 # diff --git a/.github/workflows/reusable_sycl.yml b/.github/workflows/reusable_sycl.yml index 90bdb50c8..9c727fc02 100644 --- a/.github/workflows/reusable_sycl.yml +++ b/.github/workflows/reusable_sycl.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - llvm_tag: ["latest", "nightly-2025-07-09"] # "latest" or llvm with UMF v1.0.0-rc1 + llvm_tag: ["latest", "nightly-2025-07-31"] # "latest" or llvm with UMF v1.0.0 steps: # 1. Install sycl @@ -51,7 +51,7 @@ jobs: # 2. Install UMF - name: Checkout UMF - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: path: umf_repo fetch-depth: 0 @@ -90,7 +90,7 @@ jobs: # Arbitrarily picked tests to check the compatibility. Note that some intel/llvm tests may be flaky # Checkout the repo in the version that matches the downloaded version - name: Checkout sycl - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: repository: intel/llvm path: sycl_repo diff --git a/.github/workflows/reusable_trivy.yml b/.github/workflows/reusable_trivy.yml index d7fe24fe6..75fdc9d2c 100644 --- a/.github/workflows/reusable_trivy.yml +++ b/.github/workflows/reusable_trivy.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone the git repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -38,6 +38,6 @@ jobs: cat trivy-results.sarif - name: Upload results - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/reusable_valgrind.yml b/.github/workflows/reusable_valgrind.yml index 65bb767ed..4f6076417 100644 --- a/.github/workflows/reusable_valgrind.yml +++ b/.github/workflows/reusable_valgrind.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8187a6863..f77a3e289 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: sarif_file: scorecard_results.sarif diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml index 7cb52b073..cb2e6919b 100644 --- a/.github/workflows/weekly.yml +++ b/.github/workflows/weekly.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index cfec20fa0..4b8153601 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,10 +75,6 @@ umf_option(UMF_BUILD_EXAMPLES "Build UMF examples" ON) umf_option(UMF_BUILD_GPU_EXAMPLES "Build UMF GPU examples" OFF) umf_option(UMF_BUILD_FUZZTESTS "Build UMF fuzz tests (supported only on Linux with Clang)" OFF) -umf_option( - UMF_DISABLE_HWLOC - "Disable hwloc and UMF features requiring it (OS provider, memtargets, topology discovery)" - OFF) umf_option( UMF_LINK_HWLOC_STATICALLY "Link UMF with HWLOC library statically (proxy library will be disabled on Windows+Debug build)" @@ -119,13 +115,6 @@ set_property(CACHE UMF_PROXY_LIB_BASED_ON_POOL PROPERTY STRINGS ${KNOWN_PROXY_LIB_POOLS}) list(APPEND UMF_OPTIONS_LIST UMF_PROXY_LIB_BASED_ON_POOL) -if(UMF_DISABLE_HWLOC) - message( - WARNING - "UMF_DISABLE_HWLOC option is now deprecated and will be removed in v1.1.0 UMF release!" - ) -endif() - # --------------------------------------------------------------------------- # # Setup required variables, definitions; fetch dependencies; include # sub_directories based on build options; set flags; etc. @@ -267,7 +256,7 @@ else() ) endif() -if(NOT UMF_DISABLE_HWLOC AND (NOT UMF_LINK_HWLOC_STATICALLY)) +if(NOT UMF_LINK_HWLOC_STATICALLY) pkg_check_modules(LIBHWLOC hwloc>=2.3.0) if(NOT LIBHWLOC_FOUND) find_package(LIBHWLOC 2.3.0 COMPONENTS hwloc) @@ -282,110 +271,95 @@ if(NOT UMF_DISABLE_HWLOC AND (NOT UMF_LINK_HWLOC_STATICALLY)) endif() endif() -if(UMF_LINK_HWLOC_STATICALLY AND LINUX) - find_program(AUTORECONF_EXECUTABLE autoreconf) - if(NOT AUTORECONF_EXECUTABLE) - message(WARNING "autoreconf is not installed. Disabling hwloc.") - set(UMF_DISABLE_HWLOC ON) - set(UMF_LINK_HWLOC_STATICALLY OFF) +if(UMF_LINK_HWLOC_STATICALLY) + if(NOT DEFINED UMF_HWLOC_REPO) + set(UMF_HWLOC_REPO "https://github.com/open-mpi/hwloc.git") + endif() + if(NOT DEFINED UMF_HWLOC_TAG) + set(UMF_HWLOC_TAG hwloc-2.10.0) endif() -endif() -if(UMF_DISABLE_HWLOC) - message(STATUS "hwloc is disabled, hence OS provider, memtargets, " - "topology discovery, examples won't be available!") -else() - if(UMF_LINK_HWLOC_STATICALLY) - if(NOT DEFINED UMF_HWLOC_REPO) - set(UMF_HWLOC_REPO "https://github.com/open-mpi/hwloc.git") - endif() + message( + STATUS "Will fetch hwloc from ${UMF_HWLOC_REPO} (tag: ${UMF_HWLOC_TAG})" + ) - if(NOT DEFINED UMF_HWLOC_TAG) - set(UMF_HWLOC_TAG hwloc-2.10.0) + if(WINDOWS) + set(HWLOC_ENABLE_TESTING OFF) + set(HWLOC_SKIP_LSTOPO ON) + set(HWLOC_SKIP_TOOLS ON) + set(HWLOC_SKIP_INCLUDES ON) + + FetchContent_Declare( + hwloc_targ + GIT_REPOSITORY ${UMF_HWLOC_REPO} + GIT_TAG ${UMF_HWLOC_TAG} + SOURCE_SUBDIR contrib/windows-cmake/) + FetchContent_MakeAvailable(hwloc_targ) + + set(HWLOC_LIB_PATH "") + if(CMAKE_GENERATOR STREQUAL "NMake Makefiles") + set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/hwloc.lib") + else() + set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/lib/hwloc.lib") endif() - message( - STATUS - "Will fetch hwloc from ${UMF_HWLOC_REPO} (tag: ${UMF_HWLOC_TAG})" - ) - if(WINDOWS) - set(HWLOC_ENABLE_TESTING OFF) - set(HWLOC_SKIP_LSTOPO ON) - set(HWLOC_SKIP_TOOLS ON) - set(HWLOC_SKIP_INCLUDES ON) - - FetchContent_Declare( - hwloc_targ - GIT_REPOSITORY ${UMF_HWLOC_REPO} - GIT_TAG ${UMF_HWLOC_TAG} - SOURCE_SUBDIR contrib/windows-cmake/) - FetchContent_MakeAvailable(hwloc_targ) - - set(HWLOC_LIB_PATH "") - if(CMAKE_GENERATOR STREQUAL "NMake Makefiles") - set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/hwloc.lib") - else() - set(HWLOC_LIB_PATH "${hwloc_targ_BINARY_DIR}/lib/hwloc.lib") - endif() - - get_filename_component(LIBHWLOC_LIBRARY_DIRS ${HWLOC_LIB_PATH} - DIRECTORY) - set(LIBHWLOC_LIBRARIES ${HWLOC_LIB_PATH}) - set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) - set(LIBHWLOC_FOUND TRUE) - else() # not Windows - FetchContent_Declare( - hwloc_targ - GIT_REPOSITORY ${UMF_HWLOC_REPO} - GIT_TAG ${UMF_HWLOC_TAG}) - FetchContent_MakeAvailable(hwloc_targ) - - add_custom_command( - COMMAND ./autogen.sh - WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} - OUTPUT ${hwloc_targ_SOURCE_DIR}/configure) - add_custom_command( - COMMAND - ./configure --prefix=${hwloc_targ_BINARY_DIR} - --enable-static=yes --enable-shared=no --disable-libxml2 - --disable-pci --disable-levelzero --disable-opencl - --disable-cuda --disable-nvml --disable-libudev - --disable-rsmi CFLAGS=-fPIC CXXFLAGS=-fPIC - WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} - OUTPUT ${hwloc_targ_SOURCE_DIR}/Makefile - DEPENDS ${hwloc_targ_SOURCE_DIR}/configure) - add_custom_command( - COMMAND make - WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} - OUTPUT ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la - DEPENDS ${hwloc_targ_SOURCE_DIR}/Makefile) - add_custom_command( - COMMAND make install - WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} - OUTPUT ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a - DEPENDS ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la) - - add_custom_target(hwloc_prod - DEPENDS ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) - add_library(hwloc INTERFACE) - target_link_libraries( - hwloc INTERFACE ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) - add_dependencies(hwloc hwloc_prod) - - set(LIBHWLOC_LIBRARY_DIRS ${hwloc_targ_BINARY_DIR}/lib) - set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) - set(LIBHWLOC_LIBRARIES ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) - set(LIBHWLOC_FOUND TRUE) - endif() - endif() # UMF_LINK_HWLOC_STATICALLY + get_filename_component(LIBHWLOC_LIBRARY_DIRS ${HWLOC_LIB_PATH} + DIRECTORY) + set(LIBHWLOC_LIBRARIES ${HWLOC_LIB_PATH}) + set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) + set(LIBHWLOC_FOUND TRUE) + else() # not Windows + FetchContent_Declare( + hwloc_targ + GIT_REPOSITORY ${UMF_HWLOC_REPO} + GIT_TAG ${UMF_HWLOC_TAG}) + FetchContent_MakeAvailable(hwloc_targ) - message(STATUS " LIBHWLOC_LIBRARIES = ${LIBHWLOC_LIBRARIES}") - message(STATUS " LIBHWLOC_INCLUDE_DIRS = ${LIBHWLOC_INCLUDE_DIRS}") - message(STATUS " LIBHWLOC_LIBRARY_DIRS = ${LIBHWLOC_LIBRARY_DIRS}") - message(STATUS " LIBHWLOC_API_VERSION = ${LIBHWLOC_API_VERSION}") - if(WINDOWS) - message(STATUS " LIBHWLOC_DLL_DIRS = ${LIBHWLOC_DLL_DIRS}") + add_custom_command( + COMMAND ./autogen.sh + WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} + OUTPUT ${hwloc_targ_SOURCE_DIR}/configure) + add_custom_command( + COMMAND + ./configure --prefix=${hwloc_targ_BINARY_DIR} + --enable-static=yes --enable-shared=no --disable-libxml2 + --disable-pci --disable-levelzero --disable-opencl + --disable-cuda --disable-nvml --disable-libudev --disable-rsmi + CFLAGS=-fPIC CXXFLAGS=-fPIC + WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} + OUTPUT ${hwloc_targ_SOURCE_DIR}/Makefile + DEPENDS ${hwloc_targ_SOURCE_DIR}/configure) + add_custom_command( + COMMAND make + WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} + OUTPUT ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la + DEPENDS ${hwloc_targ_SOURCE_DIR}/Makefile) + add_custom_command( + COMMAND make install + WORKING_DIRECTORY ${hwloc_targ_SOURCE_DIR} + OUTPUT ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a + DEPENDS ${hwloc_targ_SOURCE_DIR}/lib/libhwloc.la) + + add_custom_target(hwloc_prod + DEPENDS ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) + add_library(hwloc INTERFACE) + target_link_libraries(hwloc + INTERFACE ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) + add_dependencies(hwloc hwloc_prod) + + set(LIBHWLOC_LIBRARY_DIRS ${hwloc_targ_BINARY_DIR}/lib) + set(LIBHWLOC_INCLUDE_DIRS ${hwloc_targ_BINARY_DIR}/include) + set(LIBHWLOC_LIBRARIES ${hwloc_targ_BINARY_DIR}/lib/libhwloc.a) + set(LIBHWLOC_FOUND TRUE) endif() +endif() # UMF_LINK_HWLOC_STATICALLY + +message(STATUS " LIBHWLOC_LIBRARIES = ${LIBHWLOC_LIBRARIES}") +message(STATUS " LIBHWLOC_INCLUDE_DIRS = ${LIBHWLOC_INCLUDE_DIRS}") +message(STATUS " LIBHWLOC_LIBRARY_DIRS = ${LIBHWLOC_LIBRARY_DIRS}") +message(STATUS " LIBHWLOC_API_VERSION = ${LIBHWLOC_API_VERSION}") +if(WINDOWS) + message(STATUS " LIBHWLOC_DLL_DIRS = ${LIBHWLOC_DLL_DIRS}") endif() if(hwloc_targ_SOURCE_DIR) @@ -422,7 +396,7 @@ if(UMF_BUILD_LEVEL_ZERO_PROVIDER) else() set(LEVEL_ZERO_LOADER_REPO "https://github.com/oneapi-src/level-zero.git") - set(LEVEL_ZERO_LOADER_TAG v1.21.9) + set(LEVEL_ZERO_LOADER_TAG v1.22.4) message(STATUS "Fetching Level Zero loader (${LEVEL_ZERO_LOADER_TAG}) " "from ${LEVEL_ZERO_LOADER_REPO} ...") @@ -540,7 +514,7 @@ if(WINDOWS AND UMF_USE_DEBUG_POSTFIX) -DUMF_BUILD_TESTS=OFF -DUMF_BUILD_GPU_TESTS=OFF -DUMF_BUILD_BENCHMARKS=OFF -DUMF_BUILD_BENCHMARKS_MT=OFF -DUMF_BUILD_EXAMPLES=OFF -DUMF_BUILD_GPU_EXAMPLES=OFF - -DUMF_BUILD_FUZZTESTS=OFF -DUMF_DISABLE_HWLOC=${UMF_DISABLE_HWLOC} + -DUMF_BUILD_FUZZTESTS=OFF -DUMF_LINK_HWLOC_STATICALLY=${UMF_LINK_HWLOC_STATICALLY} -DUMF_HWLOC_NAME=${UMF_HWLOC_NAME} -DUMF_INSTALL_RPATH=${UMF_INSTALL_RPATH} -DUMF_DEVELOPER_MODE=OFF @@ -763,9 +737,7 @@ if(WINDOWS) endif() # set UMF_PROXY_LIB_ENABLED -if(UMF_DISABLE_HWLOC) - message(STATUS "Disabling the proxy library, because HWLOC is disabled") -elseif(NOT UMF_BUILD_SHARED_LIBRARY) +if(NOT UMF_BUILD_SHARED_LIBRARY) # TODO enable this scenario message( STATUS @@ -810,7 +782,7 @@ if(UMF_BUILD_BENCHMARKS) add_subdirectory(benchmark) endif() -if(UMF_BUILD_EXAMPLES AND NOT UMF_DISABLE_HWLOC) +if(UMF_BUILD_EXAMPLES) add_subdirectory(examples) endif() diff --git a/README.md b/README.md index dd17d762f..e73b8b727 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ List of options provided by CMake: | UMF_USE_VALGRIND | Enable Valgrind instrumentation | ON/OFF | OFF | | UMF_USE_COVERAGE | Build with coverage enabled (Linux only) | ON/OFF | OFF | | UMF_LINK_HWLOC_STATICALLY | Link UMF with HWLOC library statically (proxy library will be disabled on Windows+Debug build) | ON/OFF | OFF | -| UMF_DISABLE_HWLOC | Disable features that requires hwloc (OS provider, memory targets, topology discovery) | ON/OFF | OFF | ## Architecture: memory pools and providers diff --git a/RELEASE_STEPS.md b/RELEASE_STEPS.md index 9189d4804..32ab7b5c3 100644 --- a/RELEASE_STEPS.md +++ b/RELEASE_STEPS.md @@ -4,8 +4,8 @@ This document contains all the steps required to make a new release of UMF. As a helper, we use in this guide these 2 variables: ```bash - set $VERSION = new full version (e.g., 0.1.0-rc1) # -rc1 included just as an example - set $VER = new major+minor only version (e.g., 0.1) + $VERSION=1.1.0-rc1 # New full version, including optional rc suffix as an example + $VER=1.1 # New major+minor only version ``` **Note:** @@ -19,47 +19,63 @@ will be released for a oneAPI release. Once all changes planned for UMF release we follow the process (described in more detail below): 1. Checkout the appropriate branch (`main` or "stable" `v$VER.x`). -2. Make changes for the release. -3. Create a new tag based on the latest commit - it takes the form +1. Make sure remotes are up-to-date on your machine (`git remote update`). +1. Make changes for the release. +1. Create a new tag based on the latest commit - it should follow the format: `v..` (e.g., `v0.1.0`). -4. Push the tag and branch to the upstream. -5. Create a new GitHub release using the tag created in the previous step. -6. Update downstream projects to utilize the release tag. If any issues arise +1. Push the tag and branch to the upstream. +1. Create a new GitHub release using the tag created in the previous step. +1. Update dependent/downstream projects to use the new release tag. If any issues arise from integration, apply any necessary hot fixes to `v$VER.x` branch and go back to step 2 - to create a patch release. This step can also be tested using `rc` version, potentially followed by another `rc` tag. ## Make a release locally -Do changes for a release: -- Start of appropriate branch: +Prepare changes for the release: +- Start of appropriate up-to-date branch: + - Fetch remotes + - `git remote update` - For patch release, do it from a stable branch: - `git checkout v$VER.x` (e.g., checkout `v0.1.x` if this is a `v0.1.1` patch) - If previously we decided not to create such branch, create it now, based on the appropriate minor or major tag - For major/minor release start from the `main` branch -- Add an entry to ChangeLog, remember to change the day of the week in the release date - - For major and minor (prior 1.0.0) releases mention API and ABI compatibility with the previous release +- Add a new entry to the `ChangeLog`, remember to change the day of the week in the release date + - For major releases mention API and ABI compatibility with the previous releases - For major and minor releases, update `UMF_VERSION_CURRENT` in `include/umf/base.h` (the API version) - For changes in ops structures, update corresponding UMF_*_OPS_VERSION_CURRENT -- For major and minor (prior 1.0.0) releases update ABI version in `.map` and `.def` files +- For major and minor releases update ABI version in `.map` and `.def` files - These files are defined for all public libraries (`libumf` and `proxy_lib`, at the moment) + - For minor releases acceptable is only adding new functions/symbols! +- Once all changes are done, build locally (and/or verify changes on CI), including: + - Verify if scanners/linters/checkers passed + - Verify if version is set properly, especially in `.dll` and `.so` files - Commit these changes and tag the release: - `git commit -a -S -m "$VERSION release"` - `git tag -a -s -m "Version $VERSION" v$VERSION` +- Verify if commit and tag are properly signed: + - `git verify-commit ` + - `git verify-tag v$VERSION` - For major/minor release: - If stable branch for this release is required, create it: - `git checkout -b v$VER.x` - - For some early versions (like `0.1.0`) we may omit creation of the branch + - For some short-lived versions, creation of this branch may be skipped - For major/minor release, when release is done, add an extra "dev" tag on the `main` branch: - - `git tag -a -s -m "Development version $VERSION+1" v$VERSION+1-dev` - - for example, when `v0.1.0` is released, the dev tag would be `v0.2.0-dev` - - if needed, further in time, an extra dev tag can be introduced, e.g. `v0.2.0-dev1` + - `git tag -a -s -m "Development version $VERSION+1 - dev1" v$VERSION+1-dev1` + - for example, when `v0.1.0` is released, the dev tag would be `v0.2.0-dev1` + - if needed, further in time, an extra dev tag can be introduced, e.g. `v0.2.0-dev2` - This way, the `main` branch will introduce itself as the next version + - "dev" tag can and should be added right after we merge changes from stable to main ## Publish changes As patch releases should be done on the stable branches, pushing tags and branches differ a little. +**Note:** +> Before pushing to "upstream" it's preferred to push changes into your own fork. +> This allows you to verify the branch and tag manually in GitHub interface, and it will +> trigger the CI on your fork. + For patch release: - `git push upstream HEAD:v$VER.x v$VERSION` - push branch and tag @@ -70,13 +86,17 @@ For major/minor release: - `git checkout v$VER.x` - `git push upstream HEAD:v$VER.x` +When final release is done it's best to merge back changes from stable branch to main. +This situation can happen if the stable branch was created before the final release (e.g. +with one of the RC versions). Thanks to that all the changes, including ChangeLog will land +on the main branch. After such merge-back it's advised to add "dev" tag (described above). + ## Announce release To make the release official: - Go to [GitHub's releases tab](https://github.com/oneapi-src/unified-memory-framework/releases/new): - Tag version: `v$VERSION`, release title: UMF $VERSION, description: copy entry from ChangeLog and format it with no tabs and no characters limit in line - - Prior to version 1.0.0, check the *Set as a pre-release* tick box. -- Announce the release, where needed +- Announce the release in all appropriate channels ## More information diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 9b46ed6ea..1c417af67 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -2,22 +2,28 @@ # Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -include(FetchContent) -FetchContent_Declare( - googlebenchmark - GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG v1.9.0) - -set(BENCHMARK_ENABLE_GTEST_TESTS - OFF - CACHE BOOL "" FORCE) -set(BENCHMARK_ENABLE_TESTING - OFF - CACHE BOOL "" FORCE) -set(BENCHMARK_ENABLE_INSTALL - OFF - CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googlebenchmark) +set(GBENCH_VERSION 1.9.0) + +find_package(benchmark ${GBENCH_VERSION} QUIET) + +if(NOT benchmark_FOUND) + include(FetchContent) + FetchContent_Declare( + googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v${GBENCH_VERSION}) + + set(BENCHMARK_ENABLE_GTEST_TESTS + OFF + CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_TESTING + OFF + CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_INSTALL + OFF + CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googlebenchmark) +endif() # In MSVC builds, there is no way to determine the actual build type during the # CMake configuration step. Therefore, this message is printed in all MSVC diff --git a/docs/config/api.rst b/docs/config/api.rst index 879623428..609ffc147 100644 --- a/docs/config/api.rst +++ b/docs/config/api.rst @@ -170,6 +170,26 @@ Memtarget .. doxygenfile:: experimental/memtarget.h :sections: define enum typedef func +Memory Properties +========================================== + +Memory properties in UMF describe the characteristics and capabilities of +different memory regions or allocations. These properties can include +information such as memory type, allocation size, context and device used for +allocation, and other attributes that are relevant for memory management. + +The Memory Properties API allows users to retrieve and interpret these +attributes for memory managed by UMF, enabling advanced memory management +strategies and improved interoperability with heterogeneous systems. + +.. note:: + The memory properties APIs are experimental and may change in future releases. + +Memory Properties +------------------------------------------ +.. doxygenfile:: experimental/memory_properties.h + :sections: define enum typedef func var + Inter-Process Communication ========================================== diff --git a/docs/config/spelling_exceptions.txt b/docs/config/spelling_exceptions.txt index d4e40a3ec..3385a2216 100644 --- a/docs/config/spelling_exceptions.txt +++ b/docs/config/spelling_exceptions.txt @@ -3,22 +3,24 @@ allocatable allocator allocators calloc -CXL copyable +CUcontext +CUdevice customizable +CXL daxX -deallocation deallocating +deallocation deallocations -Devdax dev +Devdax Globals +highPtr hMemtarget hPool hProvider -highPtr -io interprocess +io ipc jemalloc lowPtr @@ -35,6 +37,7 @@ Memtarget memtarget memtargets middleware +minBytesToKeep multithreading Nodemask nodemask @@ -47,8 +50,9 @@ partList pid poolable preallocated -providerIpcData +propertyId providential +providerIpcData ptr realloc Scalable @@ -57,11 +61,16 @@ stdout Tiering tiering topologies +uint +uintptr umf umfGetIPCHandle +umfGetMemoryPropertySize umfMemoryProviderAlloc umfMemoryProviderGetLastNativeError umfMemoryProviderOpenIPCHandle +umfMemspaceMemtargetAdd +umfMemspaceUserFilter umfOsMemoryProviderParamsDestroy umfPool umfPoolCalloc @@ -71,4 +80,7 @@ umfPoolMallocUsableSize umfPoolRealloc umfMemspaceUserFilter umfMemspaceMemtargetAdd -unfreed \ No newline at end of file +unfreed +usm +zA +ze diff --git a/examples/dram_and_fsdax/dram_and_fsdax.c b/examples/dram_and_fsdax/dram_and_fsdax.c index ad2d12392..4486934d9 100644 --- a/examples/dram_and_fsdax/dram_and_fsdax.c +++ b/examples/dram_and_fsdax/dram_and_fsdax.c @@ -96,7 +96,7 @@ int main(void) { // - the UMF_TESTS_FSDAX_PATH environment variable to contain // a path to a file on this FSDAX device. char *path = getenv("UMF_TESTS_FSDAX_PATH"); - if (path == NULL || path[0] == 0) { + if (path == NULL || path[0] == '\0') { fprintf( stderr, "Warning: UMF_TESTS_FSDAX_PATH is not set, skipping testing ...\n"); diff --git a/include/umf/base.h b/include/umf/base.h index f7cd9de63..096934b18 100644 --- a/include/umf/base.h +++ b/include/umf/base.h @@ -51,6 +51,33 @@ typedef enum umf_result_t { UMF_RESULT_ERROR_UNKNOWN = 0x7ffffffe ///< Unknown error } umf_result_t; +/// @brief Handle to the memory properties structure +typedef struct umf_memory_properties_t *umf_memory_properties_handle_t; + +/// @brief ID of the memory property +typedef enum umf_memory_property_id_t { + UMF_MEMORY_PROPERTY_INVALID = -1, ///< Invalid property + + // UMF specific + UMF_MEMORY_PROPERTY_PROVIDER_HANDLE = 0, ///< Handle to the memory provider + UMF_MEMORY_PROPERTY_POOL_HANDLE = 1, ///< Handle to the memory pool + + // generic pointer properties + UMF_MEMORY_PROPERTY_BASE_ADDRESS = 10, ///< Base address of the allocation + UMF_MEMORY_PROPERTY_BASE_SIZE = 11, ///< Base size of the allocation + UMF_MEMORY_PROPERTY_BUFFER_ID = 12, ///< Unique identifier for the buffer + + // GPU specific + UMF_MEMORY_PROPERTY_POINTER_TYPE = 20, ///< Type of the pointer + UMF_MEMORY_PROPERTY_CONTEXT = 21, ///< GPU context of the allocation + UMF_MEMORY_PROPERTY_DEVICE = + 22, ///< GPU device where the allocation resides + + /// @cond + UMF_MEMORY_PROPERTY_MAX_RESERVED = 0x1000, ///< Maximum reserved value + /// @endcond +} umf_memory_property_id_t; + /// @brief Type of the CTL query typedef enum umf_ctl_query_type { CTL_QUERY_READ, diff --git a/include/umf/experimental/memory_properties.h b/include/umf/experimental/memory_properties.h new file mode 100644 index 000000000..fe44ef592 --- /dev/null +++ b/include/umf/experimental/memory_properties.h @@ -0,0 +1,64 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#ifndef UMF_MEMORY_PROPERTIES_H +#define UMF_MEMORY_PROPERTIES_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief Get the memory properties handle for a given pointer +/// \details +/// The handle returned by this function is valid until the memory pointed +/// to by the pointer is freed. +/// @param ptr pointer to the allocated memory +/// @param props_handle [out] pointer to the memory properties handle +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure +umf_result_t +umfGetMemoryPropertiesHandle(const void *ptr, + umf_memory_properties_handle_t *props_handle); + +/// @brief Get the size of a specific memory property +/// \details +/// The size of the property should be used to allocate a buffer to hold the +/// value of the property. +/// @param props_handle handle to the memory properties +/// @param memory_property_id ID of the memory property to get the size of +/// @param size [out] pointer to the size of the property +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure +umf_result_t +umfGetMemoryPropertySize(umf_memory_properties_handle_t props_handle, + umf_memory_property_id_t memory_property_id, + size_t *size); + +/// @brief Get a specific memory property from the properties handle +/// \details +/// The type of the property value depends on the property ID. The size of +/// the property value buffer must be large enough to hold the +/// value of the property. The size of the property can be obtained by +/// calling umfGetMemoryPropertySize() with the same property ID. +/// @param props_handle handle to the memory properties +/// @param memory_property_id ID of the memory property to get +/// @param property_value [out] pointer to the value of the memory property +/// which will be filled +/// @param max_property_size size of the property value buffer +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure +umf_result_t umfGetMemoryProperty(umf_memory_properties_handle_t props_handle, + umf_memory_property_id_t memory_property_id, + void *property_value, + size_t max_property_size); + +#ifdef __cplusplus +} +#endif + +#endif /* UMF_MEMORY_PROPERTIES_H */ diff --git a/include/umf/memory_pool.h b/include/umf/memory_pool.h index c405e6f61..f46784051 100644 --- a/include/umf/memory_pool.h +++ b/include/umf/memory_pool.h @@ -196,6 +196,24 @@ umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag, /// @return UMF_RESULT_SUCCESS on success. umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag); +/// +/// @brief Trims memory of the pool, removing resources that are not needed +/// to keep the pool operational. +/// \details +/// The minBytesToKeep parameter is a hint to the pool implementation +/// that it should try to keep at least this number of bytes of +/// memory in the pool. The pool implementation may also ignore this +/// parameter and try to trim the whole memory, in which case it +/// should return UMF_RESULT_SUCCESS. The pool implementation may +/// also return UMF_RESULT_ERROR_NOT_SUPPORTED if it does not support +/// trimming memory. +/// @param hPool pointer to the memory pool +/// @param minBytesToKeep minimum number of bytes to keep in the pool (if +/// possible - see details) +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool, + size_t minBytesToKeep); + #ifdef __cplusplus } #endif diff --git a/include/umf/memory_pool_ops.h b/include/umf/memory_pool_ops.h index 4cba05319..c9628c77e 100644 --- a/include/umf/memory_pool_ops.h +++ b/include/umf/memory_pool_ops.h @@ -22,7 +22,7 @@ extern "C" { /// @brief Version of the Memory Pool ops structure. /// NOTE: This is equal to the latest UMF version, in which the ops structure /// has been modified. -#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 0) +#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 1) /// /// @brief This structure comprises function pointers used by corresponding umfPool* @@ -138,12 +138,17 @@ typedef struct umf_memory_pool_ops_t { /// /// * Implementations *must* return default pool name when NULL is provided, /// otherwise the pool's name is returned. - /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. /// + /// * The returned name should not exceed 64 characters including null character and may contain + /// only [a-zA-Z0-9_-] characters. Names violating these rules are deprecated + /// and will not be supported in the next major API release. + /// CTL functionality may be limited if other characters are returned. + /// + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t (*get_name)(void *pool, const char **name); /// - /// The following function is optional and memory pool implementation + /// The following functions are optional and memory pool implementation /// can keep it NULL. /// @@ -166,6 +171,26 @@ typedef struct umf_memory_pool_ops_t { const char *name, void *arg, size_t size, umf_ctl_query_type_t queryType, va_list args); + // The following operations were added in ops version 1.1 + + /// + /// @brief Trims memory of the pool, removing resources that are not needed + /// to keep the pool operational. + /// \details + /// The minBytesToKeep parameter is a hint to the pool implementation + /// that it should try to keep at least this number of bytes of + /// memory in the pool. The pool implementation may also ignore this + /// parameter and try to trim the whole memory, in which case it + /// should return UMF_RESULT_SUCCESS. The pool implementation may + /// also return UMF_RESULT_ERROR_NOT_SUPPORTED if it does not support + /// trimming memory. + /// @param pool pointer to the memory pool + /// @param minBytesToKeep minimum number of bytes to keep in the pool (if + /// possible - see details) + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on + /// failure. + /// + umf_result_t (*ext_trim_memory)(void *pool, size_t minBytesToKeep); } umf_memory_pool_ops_t; #ifdef __cplusplus diff --git a/include/umf/memory_provider_ops.h b/include/umf/memory_provider_ops.h index 16e1536fd..a520ed889 100644 --- a/include/umf/memory_provider_ops.h +++ b/include/umf/memory_provider_ops.h @@ -21,7 +21,7 @@ extern "C" { /// @brief Version of the Memory Provider ops structure. /// NOTE: This is equal to the latest UMF version, in which the ops structure /// has been modified. -#define UMF_PROVIDER_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 0) +#define UMF_PROVIDER_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 1) /// /// @brief This structure comprises function pointers used by corresponding @@ -123,8 +123,18 @@ typedef struct umf_memory_provider_ops_t { /// @brief Retrieve name of a given memory \p provider. /// @param provider pointer to the memory provider /// @param name [out] pointer to a string containing the name of the \p provider - /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. + /// \details + /// * Implementations *must* return a literal null-terminated string. + /// + /// * Implementations *must* return default pool name when NULL is provided, + /// otherwise the pool's name is returned. /// + /// * The returned name should not exceed 64 characters and may contain + /// only [a-zA-Z0-9_-] characters. Names violating these rules are deprecated + /// and will not be supported in the next major API release. + /// CTL functionality may be limited if other characters are returned. + /// + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t (*get_name)(void *provider, const char **name); /// @@ -278,6 +288,40 @@ typedef struct umf_memory_provider_ops_t { const char *name, void *arg, size_t size, umf_ctl_query_type_t queryType, va_list args); + // The following operations were added in ops version 1.1 + + /// + /// @brief Retrieve provider-specific properties of the memory allocation. + /// \details + /// If provider supports allocation properties, + /// ext_get_allocation_properties and ext_get_allocation_properties_size, + /// must either be all set or all NULL. + /// @param provider pointer to the memory provider + /// @param ptr pointer to the allocated memory + /// @param memory_property_id ID of the memory property + /// @param property_value [out] pointer to the value of the memory property + /// which will be filled + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure + /// + umf_result_t (*ext_get_allocation_properties)( + void *provider, const void *ptr, + umf_memory_property_id_t memory_property_id, void *property_value); + + /// @brief Retrieve size of the provider-specific properties of the memory + /// allocation. + /// \details + /// If provider supports allocation properties, + /// ext_get_allocation_properties and ext_get_allocation_properties_size, + /// must either be all set or all NULL. + /// @param provider pointer to the memory provider + /// @param memory_property_id ID of the memory property to get the size of + /// @param size [out] pointer to the size of the property + /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure + /// + umf_result_t (*ext_get_allocation_properties_size)( + void *provider, umf_memory_property_id_t memory_property_id, + size_t *size); + } umf_memory_provider_ops_t; #ifdef __cplusplus diff --git a/include/umf/pools/pool_disjoint.h b/include/umf/pools/pool_disjoint.h index c7032fd60..1758fee89 100644 --- a/include/umf/pools/pool_disjoint.h +++ b/include/umf/pools/pool_disjoint.h @@ -109,7 +109,10 @@ umf_result_t umfDisjointPoolParamsSetSharedLimits( /// @brief Set custom name of the disjoint pool to be used in the traces. /// @param hParams handle to the parameters of the disjoint pool. -/// @param name custom name of the pool. Name longer than 64 characters will be truncated. +/// @param name custom name of the pool. Must not be NULL. Name longer than 63 +/// characters will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. /// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. umf_result_t umfDisjointPoolParamsSetName(umf_disjoint_pool_params_handle_t hParams, diff --git a/include/umf/pools/pool_jemalloc.h b/include/umf/pools/pool_jemalloc.h index 8d5b090d6..f7c881b6c 100644 --- a/include/umf/pools/pool_jemalloc.h +++ b/include/umf/pools/pool_jemalloc.h @@ -43,6 +43,17 @@ umf_result_t umfJemallocPoolParamsSetNumArenas(umf_jemalloc_pool_params_handle_t hParams, size_t numArenas); +/// @brief Set custom name of the jemalloc pool used in traces. +/// @param hParams handle to the parameters of the jemalloc pool. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t +umfJemallocPoolParamsSetName(umf_jemalloc_pool_params_handle_t hParams, + const char *name); + const umf_memory_pool_ops_t *umfJemallocPoolOps(void); #ifdef __cplusplus diff --git a/include/umf/pools/pool_scalable.h b/include/umf/pools/pool_scalable.h index f93e8d38e..749cd8a39 100644 --- a/include/umf/pools/pool_scalable.h +++ b/include/umf/pools/pool_scalable.h @@ -53,6 +53,17 @@ umf_result_t umfScalablePoolParamsSetKeepAllMemory(umf_scalable_pool_params_handle_t hParams, bool keepAllMemory); +/// @brief Set custom name of the scalable pool used in traces. +/// @param hParams handle to the parameters of the scalable pool. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t +umfScalablePoolParamsSetName(umf_scalable_pool_params_handle_t hParams, + const char *name); + /// @brief Return \p ops structure containing pointers to the scalable pool implementation. /// @return pointer to the \p umf_memory_pool_ops_t struct. const umf_memory_pool_ops_t *umfScalablePoolOps(void); diff --git a/include/umf/providers/provider_cuda.h b/include/umf/providers/provider_cuda.h index bbbabc2de..dbed42219 100644 --- a/include/umf/providers/provider_cuda.h +++ b/include/umf/providers/provider_cuda.h @@ -61,6 +61,16 @@ umf_result_t umfCUDAMemoryProviderParamsSetMemoryType( umf_result_t umfCUDAMemoryProviderParamsSetAllocFlags( umf_cuda_memory_provider_params_handle_t hParams, unsigned int flags); +/// @brief Set custom name of the CUDA Memory Provider. +/// @param hParams handle to the parameters of the CUDA Memory Provider. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfCUDAMemoryProviderParamsSetName( + umf_cuda_memory_provider_params_handle_t hParams, const char *name); + const umf_memory_provider_ops_t *umfCUDAMemoryProviderOps(void); #ifdef __cplusplus diff --git a/include/umf/providers/provider_devdax_memory.h b/include/umf/providers/provider_devdax_memory.h index f8557f9a3..d57b1c5de 100644 --- a/include/umf/providers/provider_devdax_memory.h +++ b/include/umf/providers/provider_devdax_memory.h @@ -56,6 +56,16 @@ umf_result_t umfDevDaxMemoryProviderParamsSetDeviceDax( umf_result_t umfDevDaxMemoryProviderParamsSetProtection( umf_devdax_memory_provider_params_handle_t hParams, unsigned protection); +/// @brief Set custom name of the Devdax Memory Provider. +/// @param hParams [in] handle to the parameters of the Devdax Memory Provider. +/// @param name [in] custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfDevDaxMemoryProviderParamsSetName( + umf_devdax_memory_provider_params_handle_t hParams, const char *name); + /// @brief Devdax Memory Provider operation results typedef enum umf_devdax_memory_provider_native_error { UMF_DEVDAX_RESULT_SUCCESS = UMF_DEVDAX_RESULTS_START_FROM, ///< Success diff --git a/include/umf/providers/provider_file_memory.h b/include/umf/providers/provider_file_memory.h index 5d0c6eb16..5586cfe7c 100644 --- a/include/umf/providers/provider_file_memory.h +++ b/include/umf/providers/provider_file_memory.h @@ -68,6 +68,16 @@ typedef enum umf_file_memory_provider_native_error { const umf_memory_provider_ops_t *umfFileMemoryProviderOps(void); +/// @brief Set custom name of the File Memory Provider. +/// @param hParams handle to the parameters of the File Memory Provider. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfFileMemoryProviderParamsSetName( + umf_file_memory_provider_params_handle_t hParams, const char *name); + #ifdef __cplusplus } #endif diff --git a/include/umf/providers/provider_fixed_memory.h b/include/umf/providers/provider_fixed_memory.h index 7c4507a27..fcedd5c00 100644 --- a/include/umf/providers/provider_fixed_memory.h +++ b/include/umf/providers/provider_fixed_memory.h @@ -51,6 +51,16 @@ umf_result_t umfFixedMemoryProviderParamsDestroy( /// @return Pointer to the umf_memory_provider_ops_t structure. const umf_memory_provider_ops_t *umfFixedMemoryProviderOps(void); +/// @brief Set custom name of the Fixed Memory Provider. +/// @param hParams [in] handle to the parameters of the Fixed Memory Provider. +/// @param name [in] custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfFixedMemoryProviderParamsSetName( + umf_fixed_memory_provider_params_handle_t hParams, const char *name); + /// @brief Fixed Memory Provider operation results typedef enum umf_fixed_memory_provider_native_error { UMF_FIXED_RESULT_SUCCESS = UMF_FIXED_RESULTS_START_FROM, ///< Success diff --git a/include/umf/providers/provider_level_zero.h b/include/umf/providers/provider_level_zero.h index 657e19ee3..65d7e3e78 100644 --- a/include/umf/providers/provider_level_zero.h +++ b/include/umf/providers/provider_level_zero.h @@ -91,6 +91,16 @@ umf_result_t umfLevelZeroMemoryProviderParamsSetDeviceOrdinal( umf_level_zero_memory_provider_params_handle_t hParams, uint32_t deviceOrdinal); +/// @brief Set custom name of the Level Zero Memory Provider. +/// @param hParams handle to the parameters of the Level Zero Memory Provider. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfLevelZeroMemoryProviderParamsSetName( + umf_level_zero_memory_provider_params_handle_t hParams, const char *name); + const umf_memory_provider_ops_t *umfLevelZeroMemoryProviderOps(void); #ifdef __cplusplus diff --git a/include/umf/providers/provider_os_memory.h b/include/umf/providers/provider_os_memory.h index 262959609..93a20173c 100644 --- a/include/umf/providers/provider_os_memory.h +++ b/include/umf/providers/provider_os_memory.h @@ -134,6 +134,17 @@ umf_result_t umfOsMemoryProviderParamsSetPartitions( umf_os_memory_provider_params_handle_t hParams, umf_numa_split_partition_t *partitions, unsigned partitions_len); +/// @brief Set custom name of the OS memory provider. +/// @param hParams handle to the parameters of the OS memory provider. +/// @param name custom name. Must not be NULL. Name longer than 63 characters +/// will be truncated. +/// \details Name should contain only [a-zA-Z0-9_-] characters. +/// Other names are deprecated and may limit CTL functionality. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t +umfOsMemoryProviderParamsSetName(umf_os_memory_provider_params_handle_t hParams, + const char *name); + /// @brief OS Memory Provider operation results typedef enum umf_os_memory_provider_native_error { UMF_OS_RESULT_SUCCESS = UMF_OS_RESULTS_START_FROM, ///< Success diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d11e04c4f..294839309 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,9 +34,7 @@ add_subdirectory(utils) add_subdirectory(base_alloc) add_subdirectory(coarse) -set(UMF_LIBS umf_utils umf_ba umf_coarse) - -set(HWLOC_DEPENDENT_SOURCES topology.c) +set(UMF_LIBS umf_utils umf_ba umf_coarse $) set(UMF_SOURCES ctl/ctl.c @@ -44,9 +42,11 @@ set(UMF_SOURCES ipc.c ipc_cache.c memory_pool.c + memory_properties.c memory_provider.c memory_provider_get_last_failed.c memtarget.c + memtargets/memtarget_numa.c mempolicy.c memspace.c memspaces/memspace_host_all.c @@ -66,7 +66,8 @@ set(UMF_SOURCES pool/pool_disjoint.c pool/pool_jemalloc.c pool/pool_proxy.c - pool/pool_scalable.c) + pool/pool_scalable.c + topology.c) if(UMF_POOL_JEMALLOC_ENABLED) set(UMF_LIBS ${UMF_LIBS} ${JEMALLOC_LIBRARIES}) @@ -78,16 +79,8 @@ if(UMF_POOL_JEMALLOC_ENABLED) "UMF_POOL_JEMALLOC_ENABLED=1") endif() -if(NOT UMF_DISABLE_HWLOC) - set(UMF_SOURCES ${UMF_SOURCES} ${HWLOC_DEPENDENT_SOURCES} - memtargets/memtarget_numa.c) - set(UMF_LIBS ${UMF_LIBS} $) - set(UMF_PRIVATE_LIBRARY_DIRS ${UMF_PRIVATE_LIBRARY_DIRS} - ${LIBHWLOC_LIBRARY_DIRS}) -else() - set(UMF_COMMON_COMPILE_DEFINITIONS ${UMF_COMMON_COMPILE_DEFINITIONS} - "UMF_NO_HWLOC=1") -endif() +set(UMF_PRIVATE_LIBRARY_DIRS ${UMF_PRIVATE_LIBRARY_DIRS} + ${LIBHWLOC_LIBRARY_DIRS}) set(UMF_SOURCES_LINUX libumf_linux.c) set(UMF_SOURCES_MACOSX libumf_linux.c) diff --git a/src/base_alloc/base_alloc.c b/src/base_alloc/base_alloc.c index 60126c9f0..9f254e675 100644 --- a/src/base_alloc/base_alloc.c +++ b/src/base_alloc/base_alloc.c @@ -128,7 +128,7 @@ static void ba_divide_memory_into_chunks(umf_ba_pool_t *pool, void *ptr, current_chunk->next = NULL; pool->metadata.free_list = ptr; // address of the first chunk - // mark the memory as unaccessible again + // mark the memory as inaccessible again utils_annotate_memory_inaccessible(ptr, size); } diff --git a/src/base_alloc/base_alloc_global.c b/src/base_alloc/base_alloc_global.c index abed0879f..c7c4e2275 100644 --- a/src/base_alloc/base_alloc_global.c +++ b/src/base_alloc/base_alloc_global.c @@ -265,3 +265,19 @@ size_t umf_ba_global_malloc_usable_size(const void *ptr) { return usable_size; } + +char *umf_ba_global_strdup(const char *s) { + if (!s) { + return NULL; + } + + size_t len = strlen(s); + + char *ptr = umf_ba_global_alloc(len + 1); + if (!ptr) { + return NULL; + } + + memcpy(ptr, s, len + 1); + return ptr; +} diff --git a/src/base_alloc/base_alloc_global.h b/src/base_alloc/base_alloc_global.h index 4cec99725..71216ec6c 100644 --- a/src/base_alloc/base_alloc_global.h +++ b/src/base_alloc/base_alloc_global.h @@ -22,6 +22,7 @@ void umf_ba_destroy_global(void); bool umf_ba_global_is_destroyed(void); size_t umf_ba_global_malloc_usable_size(const void *ptr); void *umf_ba_global_aligned_alloc(size_t size, size_t alignment); +char *umf_ba_global_strdup(const char *s); #ifdef __cplusplus } diff --git a/src/ctl/ctl.c b/src/ctl/ctl.c index b1e9de9d8..f33df885c 100644 --- a/src/ctl/ctl.c +++ b/src/ctl/ctl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,14 +28,12 @@ #include "base_alloc/base_alloc_global.h" #include "ctl_internal.h" +#include "uthash/utlist.h" #include "utils/utils_common.h" #include "utils_log.h" -#include "utlist.h" #ifdef _WIN32 #define strtok_r strtok_s -#else -#include #endif #define MAX_CONFIG_FILE_LEN (1 << 20) /* 1 megabyte */ @@ -49,13 +48,25 @@ static int ctl_global_first_free = 0; static umf_ctl_node_t CTL_NODE(global)[CTL_MAX_ENTRIES]; +static void *(*ctl_malloc_fn)(size_t) = NULL; +static void (*ctl_free_fn)(void *) = NULL; + +void ctl_init(void *(*Malloc)(size_t), void (*Free)(void *)) { + if (Malloc) { + ctl_malloc_fn = Malloc; + } + if (Free) { + ctl_free_fn = Free; + } +} + typedef struct optional_umf_result_t { bool is_valid; umf_result_t value; } optional_umf_result_t; void *Zalloc(size_t sz) { - void *ptr = umf_ba_global_alloc(sz); + void *ptr = ctl_malloc_fn(sz); if (ptr) { memset(ptr, 0, sz); } @@ -64,7 +75,7 @@ void *Zalloc(size_t sz) { char *Strdup(const char *s) { size_t len = strlen(s) + 1; - char *p = umf_ba_global_alloc(len); + char *p = ctl_malloc_fn(len); if (p) { memcpy(p, s, len); } @@ -84,7 +95,7 @@ char *Strdup(const char *s) { } \ case CTL_ARG_TYPE_STRING: { \ char *str = va_arg(va, char *); \ - memcpy(output, str, ctl_argument->dest_size); \ + snprintf((char *)output, ctl_argument->dest_size, "%s", str); \ break; \ } \ case CTL_ARG_TYPE_INT: { \ @@ -97,9 +108,14 @@ char *Strdup(const char *s) { *(long long *)output = ll; \ break; \ } \ + case CTL_ARG_TYPE_UNSIGNED_LONG_LONG: { \ + unsigned long long ll = va_arg(va, unsigned long long); \ + *(unsigned long long *)output = ll; \ + break; \ + } \ case CTL_ARG_TYPE_PTR: { \ - void *p = va_arg(va, void *); \ - *(uintptr_t *)output = (uintptr_t)p; \ + void *ptr = va_arg(va, void *); \ + *(uintptr_t *)output = (uintptr_t)ptr; \ break; \ } \ default: \ @@ -121,9 +137,9 @@ static void ctl_delete_indexes(umf_ctl_index_utlist_t *indexes) { LL_DELETE(indexes, elem); if (elem) { if (elem->arg) { - umf_ba_global_free(elem->arg); + ctl_free_fn(elem->arg); } - umf_ba_global_free(elem); + ctl_free_fn(elem); } } } @@ -139,7 +155,7 @@ static void ctl_query_cleanup_real_args(const umf_ctl_node_t *n, void *real_arg, switch (source) { case CTL_QUERY_CONFIG_INPUT: - umf_ba_global_free(real_arg); + ctl_free_fn(real_arg); break; case CTL_QUERY_PROGRAMMATIC: break; @@ -153,7 +169,7 @@ static void ctl_query_cleanup_real_args(const umf_ctl_node_t *n, void *real_arg, * structure */ static void *ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) { - char *dest_arg = umf_ba_global_alloc(arg_proto->dest_size); + char *dest_arg = ctl_malloc_fn(arg_proto->dest_size); if (dest_arg == NULL) { return NULL; } @@ -162,9 +178,6 @@ static void *ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) { char *arg_sep = strtok_r(arg, CTL_VALUE_ARG_SEPARATOR, &sptr); for (const struct ctl_argument_parser *p = arg_proto->parsers; p->parser != NULL; ++p) { - if (arg_sep == NULL) { - goto error_parsing; - } if (p->parser(arg_sep, dest_arg + p->dest_offset, p->dest_size) != 0) { goto error_parsing; @@ -176,7 +189,7 @@ static void *ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) { return dest_arg; error_parsing: - umf_ba_global_free(dest_arg); + ctl_free_fn(dest_arg); return NULL; } @@ -364,31 +377,73 @@ ctl_find_and_execute_node(const umf_ctl_node_t *nodes, void *ctx, // if the node has an argument, but no next node, then it is an error goto error; } - void *node_arg; - if (strcmp(next_node, CTL_WILDCARD) == 0) { - if (source == CTL_QUERY_CONFIG_INPUT) { - LOG_ERR( - "ctl {} wildcard is not supported for config input"); - goto error; - } - // argument is a wildcard so we need to allocate it from va_list - node_arg = umf_ba_global_alloc(n->arg->dest_size); - if (node_arg == NULL) { - goto error; - } - pop_va_list(args, n->arg, node_arg); - } else { - node_arg = ctl_parse_args(n->arg, next_node); - if (node_arg == NULL) { - goto error; + char *node_arg = ctl_malloc_fn(n->arg->dest_size); + if (node_arg == NULL) { + goto error; + } + + // Parse this argument. It might contain "struct" which is series of fields separated by comma. + // each field contains separate parser in the parsers array. + for (const struct ctl_argument_parser *p = n->arg->parsers; + p->dest_size != 0; ++p) { + + if (next_node && strcmp(next_node, CTL_WILDCARD) == 0) { + if (source == CTL_QUERY_CONFIG_INPUT) { + ctl_free_fn(node_arg); + LOG_ERR("ctl {} wildcard is not supported for config " + "input"); + goto error; + } + + if (p->type == CTL_ARG_TYPE_UNKNOWN) { + ctl_free_fn(node_arg); + LOG_ERR("ctl {} wildcard is not supported for node: %s", + node_name); + goto error; + } + char *output = node_arg + p->dest_offset; + pop_va_list(args, p, output); + } else { + if (!p->parser) { + LOG_ERR( + "this node can be passed only as {} wildcard: %s", + next_node); + ctl_free_fn(node_arg); + goto error; + } + int r = p->parser(next_node, node_arg + p->dest_offset, + p->dest_size); + if (r < 0) { + // Parsing failed — cleanup and propagate error + ctl_free_fn(node_arg); + goto error; + } else if (r > 0) { + // Parser did not consume next_node, which means this argument is optional + // and not present. Optional arguments are always at the end of the expected + // sequence, so we can safely stop parsing here. + // + // Example: + // Given two paths: + // "umf.pool.by_name.name.stats.allocs" + // "umf.pool.by_name.name.1.stats.allocs" + // The parser for 'by_name' expects the next node is string followed by optional + // integer index, if its sees "stats" instead of integer, like in second example + // it will return >0 to signal that the optional + // integer argument is not present. + // This allows the remaining nodes ("stats.allocs") to be parsed normally + // without treating "stats" as part of 'by_name'. + break; + } } + // we parsed next_node as an argument so we next one + next_node = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); } umf_ctl_index_utlist_t *entry = NULL; - entry = umf_ba_global_alloc(sizeof(*entry)); + entry = ctl_malloc_fn(sizeof(*entry)); if (entry == NULL) { - umf_ba_global_free(arg); + ctl_free_fn(node_arg); goto error; } @@ -397,8 +452,7 @@ ctl_find_and_execute_node(const umf_ctl_node_t *nodes, void *ctx, entry->arg_size = n->arg->dest_size; LL_APPEND(indexes, entry); - // we parsed next_node as an argument so we next one - next_node = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); + if (next_node == NULL) { // last node was a node with arg, but there is no next mode. // check if there is nameless leaf on next level @@ -462,13 +516,13 @@ ctl_find_and_execute_node(const umf_ctl_node_t *nodes, void *ctx, } } out: - umf_ba_global_free(parse_str); + ctl_free_fn(parse_str); ctl_delete_indexes(indexes); return ret; error: ctl_delete_indexes(indexes); - umf_ba_global_free(parse_str); + ctl_free_fn(parse_str); ret.is_valid = false; return ret; } @@ -566,7 +620,7 @@ static umf_result_t ctl_load_config_helper(struct ctl *ctl, void *ctx, // we do not need to copy va_list before call as we know that for query_config_input // ctl_query will not call va_arg on it. Ref 7.15/3 of C99 standard ret = ctl_query(ctl, ctx, CTL_QUERY_CONFIG_INPUT, name, CTL_QUERY_WRITE, - value, 0, empty_args); + value, strlen(value) + 1, empty_args); if (ret != UMF_RESULT_SUCCESS && ctx != NULL) { goto end; @@ -599,7 +653,7 @@ umf_result_t ctl_load_config_from_string(struct ctl *ctl, void *ctx, umf_result_t ret = ctl_load_config(ctl, ctx, buf); - umf_ba_global_free(buf); + ctl_free_fn(buf); return ret; } @@ -609,7 +663,6 @@ umf_result_t ctl_load_config_from_string(struct ctl *ctl, void *ctx, * This function opens up the config file, allocates a buffer of size equal to * the size of the file, reads its content and sanitizes it for ctl_load_config. */ -#ifndef _WIN32 // TODO: implement for Windows umf_result_t ctl_load_config_from_file(struct ctl *ctl, void *ctx, const char *cfg_file) { umf_result_t ret = UMF_RESULT_ERROR_UNKNOWN; @@ -661,13 +714,28 @@ umf_result_t ctl_load_config_from_file(struct ctl *ctl, void *ctx, ret = ctl_load_config(ctl, ctx, buf); - umf_ba_global_free(buf); + ctl_free_fn(buf); error_file_parse: (void)fclose(fp); return ret; } -#endif + +/* + * ctl_parse_ull -- (internal) parses and returns an unsigned long long + */ +static unsigned long long ctl_parse_ull(const char *str) { + char *endptr; + int olderrno = errno; + errno = 0; + unsigned long long val = strtoull(str, &endptr, 0); + if (endptr == str || errno != 0) { + return ULLONG_MAX; + } + errno = olderrno; + + return val; +} /* * ctl_parse_ll -- (internal) parses and returns a long long signed integer @@ -692,6 +760,9 @@ static long long ctl_parse_ll(const char *str) { int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) { /* suppress unused-parameter errors */ (void)dest_size; + if (!arg) { + return -1; + } int *intp = dest; char in = ((const char *)arg)[0]; @@ -707,10 +778,49 @@ int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) { return -1; } +/* + * ctl_arg_unsigned -- parses unsigned integer argument + */ +int ctl_arg_unsigned(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } + + unsigned long long val = ctl_parse_ull(arg); + if (val == ULLONG_MAX) { + return -1; + } + + switch (dest_size) { + case sizeof(unsigned int): + if (val > UINT_MAX) { + return -1; + } + *(unsigned int *)dest = (unsigned int)val; + break; + case sizeof(unsigned long long): + *(unsigned long long *)dest = val; + break; + case sizeof(uint8_t): + if (val > UINT8_MAX) { + return -1; + } + *(uint8_t *)dest = (uint8_t)val; + break; + default: + return -1; + } + + return 0; +} + /* * ctl_arg_integer -- parses signed integer argument */ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } long long val = ctl_parse_ll(arg); if (val == LLONG_MIN) { return -1; @@ -726,12 +836,6 @@ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { case sizeof(long long): *(long long *)dest = val; break; - case sizeof(uint8_t): - if (val > UINT8_MAX || val < 0) { - return -1; - } - *(uint8_t *)dest = (uint8_t)val; - break; default: return -1; } @@ -744,6 +848,10 @@ int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { * buffer */ int ctl_arg_string(const void *arg, void *dest, size_t dest_size) { + if (!arg) { + return -1; + } + /* check if the incoming string is longer or equal to dest_size */ if (strnlen(arg, dest_size) == dest_size) { return -1; diff --git a/src/ctl/ctl_internal.h b/src/ctl/ctl_internal.h index a45fa7732..a043b01c4 100644 --- a/src/ctl/ctl_internal.h +++ b/src/ctl/ctl_internal.h @@ -56,37 +56,39 @@ enum ctl_node_type { typedef int (*ctl_arg_parser)(const void *arg, void *dest, size_t dest_size); -struct ctl_argument_parser { - size_t dest_offset; /* offset of the field inside of the argument */ - size_t dest_size; /* size of the field inside of the argument */ - ctl_arg_parser parser; -}; typedef enum ctl_arg_type { CTL_ARG_TYPE_UNKNOWN = 0, CTL_ARG_TYPE_BOOLEAN, CTL_ARG_TYPE_STRING, CTL_ARG_TYPE_INT, CTL_ARG_TYPE_LONG_LONG, + CTL_ARG_TYPE_UNSIGNED_LONG_LONG, CTL_ARG_TYPE_PTR, MAX_CTL_ARG_TYPE } ctl_arg_type_t; +struct ctl_argument_parser { + size_t dest_offset; /* offset of the field inside of the argument */ + size_t dest_size; /* size of the field inside of the argument */ + ctl_arg_type_t type; /* type of the argument */ + ctl_arg_parser parser; +}; + struct ctl_argument { size_t dest_size; /* size of the entire argument */ - ctl_arg_type_t type; /* type of the argument */ struct ctl_argument_parser parsers[]; /* array of 'fields' in arg */ }; -#define sizeof_member(t, m) sizeof(((t *)0)->m) +#define sizeof_member(type, member) sizeof(((type *)0)->member) -#define CTL_ARG_PARSER(t, p) \ - { 0, sizeof(t), p } +#define CTL_ARG_PARSER(type, vaarg_type, parser) \ + { 0, sizeof(type), vaarg_type, parser } -#define CTL_ARG_PARSER_STRUCT(t, m, p) \ - { offsetof(t, m), sizeof_member(t, m), p } +#define CTL_ARG_PARSER_STRUCT(type, member, vaarg_type, parser) \ + { offsetof(type, member), sizeof_member(type, member), vaarg_type, parser } #define CTL_ARG_PARSER_END \ - { 0, 0, NULL } + { 0, 0, 0, NULL } /* * CTL Tree node structure, do not use directly. All the necessary functionality @@ -121,7 +123,7 @@ struct ctl { int first_free; }; -void initialize_global_ctl(void); +void ctl_init(void *(*Malloc)(size_t), void (*Free)(void *)); umf_result_t ctl_load_config_from_string(struct ctl *ctl, void *ctx, const char *cfg_string); @@ -133,39 +135,55 @@ void ctl_register_module_node(struct ctl *c, const char *name, struct ctl_node *n); int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size); +int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); +int ctl_arg_unsigned(const void *arg, void *dest, size_t dest_size); +int ctl_arg_string(const void *arg, void *dest, size_t dest_size); + #define CTL_ARG_BOOLEAN \ { \ - sizeof(int), CTL_ARG_TYPE_BOOLEAN, { \ - {0, sizeof(int), ctl_arg_boolean}, CTL_ARG_PARSER_END \ + sizeof(int), { \ + {0, sizeof(int), CTL_ARG_TYPE_BOOLEAN, ctl_arg_boolean}, \ + CTL_ARG_PARSER_END \ } \ } -int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); #define CTL_ARG_INT \ { \ - sizeof(int), CTL_ARG_TYPE_INT, { \ - {0, sizeof(int), ctl_arg_integer}, CTL_ARG_PARSER_END \ + sizeof(int), { \ + {0, sizeof(int), CTL_ARG_TYPE_INT, ctl_arg_integer}, \ + CTL_ARG_PARSER_END \ } \ } #define CTL_ARG_LONG_LONG \ { \ - sizeof(long long), CTL_ARG_TYPE_LONG_LONG, { \ - {0, sizeof(long long), ctl_arg_integer}, CTL_ARG_PARSER_END \ + sizeof(long long), { \ + {0, sizeof(long long), CTL_ARG_TYPE_LONG_LONG, ctl_arg_integer}, \ + CTL_ARG_PARSER_END \ + } \ + } + +#define CTL_ARG_UNSIGNED_LONG_LONG \ + { \ + sizeof(unsigned long long), { \ + {0, sizeof(unsigned long long), CTL_ARG_TYPE_UNSIGNED_LONG_LONG, \ + ctl_arg_unsigned}, \ + CTL_ARG_PARSER_END \ } \ } -int ctl_arg_string(const void *arg, void *dest, size_t dest_size); #define CTL_ARG_STRING(len) \ { \ - len, CTL_ARG_TYPE_PTR, { \ - {0, len, ctl_arg_string}, CTL_ARG_PARSER_END \ + len, { \ + {0, len, CTL_ARG_TYPE_STRING, ctl_arg_string}, CTL_ARG_PARSER_END \ } \ } #define CTL_ARG_PTR \ { \ - sizeof(void *), CTL_ARG_TYPE_PTR, { {0, 0, NULL}, CTL_ARG_PARSER_END } \ + sizeof(void *), { \ + {0, sizeof(void *), CTL_ARG_TYPE_PTR, NULL}, CTL_ARG_PARSER_END \ + } \ } #define _CTL_STR(name) #name diff --git a/src/ipc.c b/src/ipc.c index d4e5cc806..29ed5bac2 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -58,14 +58,19 @@ umf_result_t umfGetIPCHandle(const void *ptr, umf_ipc_handle_t *umfIPCHandle, } size_t ipcHandleSize = 0; - umf_alloc_info_t allocInfo; - umf_result_t ret = umfMemoryTrackerGetAllocInfo(ptr, &allocInfo); + umf_memory_properties_handle_t props = NULL; + umf_result_t ret = umfGetMemoryPropertiesHandle(ptr, &props); if (ret != UMF_RESULT_SUCCESS) { - LOG_ERR("cannot get alloc info for ptr = %p.", ptr); + LOG_ERR("cannot get alloc props for ptr = %p.", ptr); return ret; } - ret = umfPoolGetIPCHandleSize(allocInfo.pool, &ipcHandleSize); + if (props == NULL || props->pool == NULL) { + LOG_ERR("cannot get pool from alloc info for ptr = %p.", ptr); + return UMF_RESULT_ERROR_UNKNOWN; + } + + ret = umfPoolGetIPCHandleSize(props->pool, &ipcHandleSize); if (ret != UMF_RESULT_SUCCESS) { LOG_ERR("cannot get IPC handle size."); return ret; @@ -79,11 +84,14 @@ umf_result_t umfGetIPCHandle(const void *ptr, umf_ipc_handle_t *umfIPCHandle, // We cannot use umfPoolGetMemoryProvider function because it returns // upstream provider but we need tracking one - umf_memory_provider_handle_t provider = allocInfo.pool->provider; - assert(provider); + if (props->pool->provider == NULL) { + LOG_ERR("cannot get memory provider from pool"); + umf_ba_global_free(ipcData); + return UMF_RESULT_ERROR_UNKNOWN; + } + umf_memory_provider_handle_t provider = props->pool->provider; - ret = umfMemoryProviderGetIPCHandle(provider, allocInfo.base, - allocInfo.baseSize, + ret = umfMemoryProviderGetIPCHandle(provider, props->base, props->base_size, (void *)ipcData->providerIpcData); if (ret != UMF_RESULT_SUCCESS) { LOG_ERR("failed to get IPC handle."); @@ -92,10 +100,10 @@ umf_result_t umfGetIPCHandle(const void *ptr, umf_ipc_handle_t *umfIPCHandle, } // ipcData->handle_id is filled by tracking provider - ipcData->base = allocInfo.base; + ipcData->base = props->base; ipcData->pid = utils_getpid(); - ipcData->baseSize = allocInfo.baseSize; - ipcData->offset = (uintptr_t)ptr - (uintptr_t)allocInfo.base; + ipcData->baseSize = props->base_size; + ipcData->offset = (uintptr_t)ptr - (uintptr_t)props->base; *umfIPCHandle = ipcData; *size = ipcHandleSize; diff --git a/src/libumf.c b/src/libumf.c index 9df7ee29c..de87935aa 100644 --- a/src/libumf.c +++ b/src/libumf.c @@ -12,7 +12,9 @@ #include #include "base_alloc_global.h" +#include "ctl/ctl_internal.h" #include "ipc_cache.h" +#include "libumf.h" #include "memory_pool_internal.h" #include "memory_provider_internal.h" #include "memspace_internal.h" @@ -20,12 +22,10 @@ #include "provider_cuda_internal.h" #include "provider_level_zero_internal.h" #include "provider_tracking.h" +#include "topology.h" #include "utils_common.h" #include "utils_concurrency.h" #include "utils_log.h" -#if !defined(UMF_NO_HWLOC) -#include "topology.h" -#endif umf_memory_tracker_handle_t TRACKER = NULL; @@ -36,9 +36,25 @@ static UTIL_ONCE_FLAG initMutexOnce = UTIL_ONCE_FLAG_INIT; static void initialize_init_mutex(void) { utils_mutex_init(&initMutex); } static umf_ctl_node_t CTL_NODE(umf)[] = {CTL_CHILD(provider), CTL_CHILD(pool), - CTL_NODE_END}; + CTL_CHILD(logger), CTL_NODE_END}; + +void initialize_ctl(void) { + ctl_init(umf_ba_global_alloc, umf_ba_global_free); -void initialize_global_ctl(void) { CTL_REGISTER_MODULE(NULL, umf); } + CTL_REGISTER_MODULE(NULL, umf); + const char *env_var = getenv("UMF_CONF"); + if (env_var && env_var[0] != '\0') { + LOG_INFO("Loading UMF configuration from environment variable: %s", + env_var); + ctl_load_config_from_string(NULL, NULL, env_var); + } + + const char *file_var = getenv("UMF_CONF_FILE"); + if (file_var && file_var[0] != '\0') { + LOG_INFO("Loading UMF configuration from file: %s", file_var); + ctl_load_config_from_file(NULL, NULL, file_var); + } +} umf_result_t umfInit(void) { utils_init_once(&initMutexOnce, initialize_init_mutex); @@ -47,6 +63,8 @@ umf_result_t umfInit(void) { if (umfRefCount == 0) { utils_log_init(); + initialize_ctl(); + umf_result_t umf_result = umfMemoryTrackerCreate(&TRACKER); if (umf_result != UMF_RESULT_SUCCESS) { LOG_ERR("Failed to create memory tracker"); @@ -65,7 +83,6 @@ umf_result_t umfInit(void) { } LOG_DEBUG("UMF IPC cache initialized"); - initialize_global_ctl(); } umfRefCount++; @@ -92,7 +109,7 @@ umf_result_t umfTearDown(void) { } if (--umfRefCount == 0) { -#if !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#if !defined(_WIN32) umfMemspaceHostAllDestroy(); umfMemspaceHighestCapacityDestroy(); umfMemspaceHighestBandwidthDestroy(); @@ -115,6 +132,8 @@ umf_result_t umfTearDown(void) { umfMemoryTrackerDestroy(t); LOG_DEBUG("UMF tracker destroyed"); + umfPoolCtlDefaultsDestroy(); + umf_ba_destroy_global(); LOG_DEBUG("UMF base allocator destroyed"); @@ -131,6 +150,7 @@ umf_result_t umfTearDown(void) { int umfGetCurrentVersion(void) { return UMF_VERSION_CURRENT; } umf_result_t umfCtlGet(const char *name, void *arg, size_t size, ...) { + libumfInit(); // ctx can be NULL when getting defaults if (name == NULL || arg == NULL || size == 0) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; @@ -145,6 +165,7 @@ umf_result_t umfCtlGet(const char *name, void *arg, size_t size, ...) { } umf_result_t umfCtlSet(const char *name, void *arg, size_t size, ...) { + libumfInit(); // ctx can be NULL when setting defaults if (name == NULL || arg == NULL || size == 0) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; @@ -158,6 +179,7 @@ umf_result_t umfCtlSet(const char *name, void *arg, size_t size, ...) { } umf_result_t umfCtlExec(const char *name, void *arg, size_t size, ...) { + libumfInit(); // arg can be NULL when executing a command // ctx can be NULL when executing defaults // size can depends on the arg diff --git a/src/libumf.def b/src/libumf.def index 0159ddbe2..68163c6b5 100644 --- a/src/libumf.def +++ b/src/libumf.def @@ -6,7 +6,7 @@ LIBRARY UMF -VERSION 1.0 +VERSION 1.1 EXPORTS DllMain @@ -144,3 +144,16 @@ EXPORTS umfJemallocPoolParamsDestroy umfJemallocPoolParamsSetNumArenas umfPoolGetName +; Added in UMF_1.1 + umfCUDAMemoryProviderParamsSetName + umfDevDaxMemoryProviderParamsSetName + umfFileMemoryProviderParamsSetName + umfFixedMemoryProviderParamsSetName + umfGetMemoryPropertiesHandle + umfGetMemoryProperty + umfGetMemoryPropertySize + umfJemallocPoolParamsSetName + umfLevelZeroMemoryProviderParamsSetName + umfOsMemoryProviderParamsSetName + umfPoolTrimMemory + umfScalablePoolParamsSetName diff --git a/src/libumf.map b/src/libumf.map index 348675ff0..98b913499 100644 --- a/src/libumf.map +++ b/src/libumf.map @@ -141,3 +141,18 @@ UMF_1.0 { local: *; }; + +UMF_1.1 { + umfCUDAMemoryProviderParamsSetName; + umfDevDaxMemoryProviderParamsSetName; + umfFileMemoryProviderParamsSetName; + umfFixedMemoryProviderParamsSetName; + umfGetMemoryPropertiesHandle; + umfGetMemoryProperty; + umfGetMemoryPropertySize; + umfJemallocPoolParamsSetName; + umfLevelZeroMemoryProviderParamsSetName; + umfOsMemoryProviderParamsSetName; + umfPoolTrimMemory; + umfScalablePoolParamsSetName; +} UMF_1.0; diff --git a/src/memory_pool.c b/src/memory_pool.c index 004f42d9e..cd3f15522 100644 --- a/src/memory_pool.c +++ b/src/memory_pool.c @@ -24,19 +24,141 @@ #include "utils_assert.h" #include "utils_concurrency.h" #include "utils_log.h" - -#define UMF_DEFAULT_SIZE 100 -#define UMF_DEFAULT_LEN 100 +#include "utils_name.h" +#include "utlist.h" + +// Handle UTHash memory allocation failures without aborting the process. +#define HASH_NONFATAL_OOM 1 +static bool uthash_oom = false; +#define uthash_nonfatal_oom(obj) \ + do { \ + (void)(obj); \ + uthash_oom = true; \ + } while (0) + +#include "uthash.h" + +typedef struct ctl_default_entry_t { + char *name; + void *value; + size_t value_size; + umf_ctl_query_source_t source; + struct ctl_default_entry_t *next; +} ctl_default_entry_t; + +static ctl_default_entry_t *ctl_default_list = NULL; utils_mutex_t ctl_mtx; static UTIL_ONCE_FLAG mem_pool_ctl_initialized = UTIL_ONCE_FLAG_INIT; -char CTL_DEFAULT_ENTRIES[UMF_DEFAULT_SIZE][UMF_DEFAULT_LEN] = {0}; -char CTL_DEFAULT_VALUES[UMF_DEFAULT_SIZE][UMF_DEFAULT_LEN] = {0}; - static struct ctl umf_pool_ctl_root; -static void ctl_init(void); +static void pool_ctl_init(void); + +typedef struct pool_name_list_entry_t { + umf_memory_pool_handle_t pool; + struct pool_name_list_entry_t *next; +} pool_name_list_entry_t; + +typedef struct pool_name_dict_entry_t { + char *name; /* key */ + pool_name_list_entry_t *pools; + UT_hash_handle hh; +} pool_name_dict_entry_t; + +static pool_name_dict_entry_t *pools_by_name = NULL; +static utils_rwlock_t pools_by_name_lock; +static UTIL_ONCE_FLAG pools_by_name_init_once = UTIL_ONCE_FLAG_INIT; + +static void pools_by_name_init(void) { utils_rwlock_init(&pools_by_name_lock); } + +static umf_result_t pools_by_name_add(umf_memory_pool_handle_t pool) { + const char *name = NULL; + umf_result_t ret = pool->ops.get_name(pool->pool_priv, &name); + if (ret != UMF_RESULT_SUCCESS || !name) { + return ret; + } + + if (!utils_name_is_valid(name)) { + LOG_ERR("Pool name: %s contains invalid character, ctl by_name is not " + "supported for this pool", + name); + return UMF_RESULT_SUCCESS; + } + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + utils_write_lock(&pools_by_name_lock); + + pool_name_dict_entry_t *entry = NULL; + HASH_FIND_STR(pools_by_name, name, entry); + if (!entry) { + entry = umf_ba_global_alloc(sizeof(*entry)); + if (!entry) { + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + entry->name = umf_ba_global_strdup(name); + if (!entry->name) { + umf_ba_global_free(entry); + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + + entry->pools = NULL; + uthash_oom = false; + HASH_ADD_KEYPTR(hh, pools_by_name, entry->name, strlen(entry->name), + entry); + if (uthash_oom) { + umf_ba_global_free(entry->name); + umf_ba_global_free(entry); + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + } + + pool_name_list_entry_t *node = umf_ba_global_alloc(sizeof(*node)); + if (!node) { + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + node->pool = pool; + node->next = NULL; + LL_APPEND(entry->pools, node); + + utils_write_unlock(&pools_by_name_lock); + return UMF_RESULT_SUCCESS; +} + +static void pools_by_name_remove(umf_memory_pool_handle_t pool) { + const char *name = NULL; + if (pool->ops.get_name(pool->pool_priv, &name) != UMF_RESULT_SUCCESS || + !name) { + return; + } + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + utils_write_lock(&pools_by_name_lock); + + pool_name_dict_entry_t *entry = NULL; + HASH_FIND_STR(pools_by_name, name, entry); + if (entry) { + pool_name_list_entry_t *it = NULL, *tmp = NULL; + LL_FOREACH_SAFE(entry->pools, it, tmp) { + if (it->pool == pool) { + LL_DELETE(entry->pools, it); + umf_ba_global_free(it); + break; + } + } + if (entry->pools == NULL) { + HASH_DEL(pools_by_name, entry); + umf_ba_global_free(entry->name); + umf_ba_global_free(entry); + } + } + + utils_write_unlock(&pools_by_name_lock); +} static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_handle)( void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, @@ -66,7 +188,7 @@ static umf_result_t CTL_SUBTREE_HANDLER(default)( umf_ctl_index_utlist_t *indexes, const char *extra_name, umf_ctl_query_type_t queryType, va_list args) { (void)indexes, (void)source, (void)ctx, (void)args; - utils_init_once(&mem_pool_ctl_initialized, ctl_init); + utils_init_once(&mem_pool_ctl_initialized, pool_ctl_init); if (strstr(extra_name, "{}") != NULL) { // We might implement it in future - it requires store copy of va_list @@ -79,36 +201,76 @@ static umf_result_t CTL_SUBTREE_HANDLER(default)( utils_mutex_lock(&ctl_mtx); + ctl_default_entry_t *entry = NULL; + LL_FOREACH(ctl_default_list, entry) { + if (strcmp(entry->name, extra_name) == 0) { + break; + } + } + if (queryType == CTL_QUERY_WRITE) { - int i = 0; - for (; i < UMF_DEFAULT_SIZE; i++) { - if (CTL_DEFAULT_ENTRIES[i][0] == '\0' || - strcmp(CTL_DEFAULT_ENTRIES[i], extra_name) == 0) { - strncpy(CTL_DEFAULT_ENTRIES[i], extra_name, UMF_DEFAULT_LEN); - CTL_DEFAULT_ENTRIES[i][UMF_DEFAULT_LEN - 1] = '\0'; - strncpy(CTL_DEFAULT_VALUES[i], arg, UMF_DEFAULT_LEN); - CTL_DEFAULT_VALUES[i][UMF_DEFAULT_LEN - 1] = '\0'; - break; + bool is_new_entry = false; + if (entry == NULL) { + entry = umf_ba_global_alloc(sizeof(*entry)); + if (entry == NULL) { + utils_mutex_unlock(&ctl_mtx); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + + entry->name = NULL; + entry->value = NULL; + entry->next = NULL; + is_new_entry = true; } - if (UMF_DEFAULT_SIZE == i) { - LOG_ERR("Default entries array is full"); + + size_t name_len = strlen(extra_name) + 1; + char *new_name = umf_ba_global_alloc(name_len); + if (new_name == NULL) { utils_mutex_unlock(&ctl_mtx); - return UMF_RESULT_ERROR_OUT_OF_RESOURCES; + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } - } else if (queryType == CTL_QUERY_READ) { - int i = 0; - for (; i < UMF_DEFAULT_SIZE; i++) { - if (strcmp(CTL_DEFAULT_ENTRIES[i], extra_name) == 0) { - strncpy(arg, CTL_DEFAULT_VALUES[i], size); - break; + + memcpy(new_name, extra_name, name_len); + if (entry->name) { + umf_ba_global_free(entry->name); + } + entry->name = new_name; + + void *new_value = NULL; + if (size > 0) { + new_value = umf_ba_global_alloc(size); + if (new_value == NULL) { + utils_mutex_unlock(&ctl_mtx); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + memcpy(new_value, arg, size); } - if (UMF_DEFAULT_SIZE == i) { + + if (entry->value) { + umf_ba_global_free(entry->value); + } + + entry->value = new_value; + entry->value_size = size; + entry->source = source; + + if (is_new_entry) { + LL_APPEND(ctl_default_list, entry); + } + } else if (queryType == CTL_QUERY_READ) { + if (entry == NULL) { LOG_WARN("Wrong path name: %s", extra_name); utils_mutex_unlock(&ctl_mtx); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } + + if (entry->value_size > size) { + LOG_ERR("Provided buffer size %zu is smaller than field size %zu", + size, entry->value_size); + utils_mutex_unlock(&ctl_mtx); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + memcpy(arg, entry->value, entry->value_size); } utils_mutex_unlock(&ctl_mtx); @@ -145,10 +307,137 @@ static umf_ctl_node_t CTL_NODE(by_handle)[] = { static const struct ctl_argument CTL_ARG(by_handle) = CTL_ARG_PTR; +typedef struct by_name_arg_t { + char name[255]; + size_t index; +} by_name_arg_t; + +// parses optional size_t argument. if arg is not integer then sets out to size_max +static int by_name_index_parser(const void *arg, void *dest, size_t dest_size) { + size_t *out = (size_t *)dest; + + if (arg == NULL) { + *out = SIZE_MAX; + return 1; // node n + } + + int ret = ctl_arg_unsigned(arg, dest, dest_size); + if (ret) { + *out = SIZE_MAX; + return 1; + } + + return 0; +} + +static const struct ctl_argument CTL_ARG(by_name) = { + sizeof(by_name_arg_t), + {CTL_ARG_PARSER_STRUCT(by_name_arg_t, name, CTL_ARG_TYPE_STRING, + ctl_arg_string), + CTL_ARG_PARSER_STRUCT(by_name_arg_t, index, + CTL_ARG_TYPE_UNSIGNED_LONG_LONG, + by_name_index_parser), + CTL_ARG_PARSER_END}}; + +static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_name)( + void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, + umf_ctl_index_utlist_t *indexes, const char *extra_name, + umf_ctl_query_type_t queryType, va_list args) { + (void)ctx; + + utils_init_once(&pools_by_name_init_once, pools_by_name_init); + + by_name_arg_t *name_arg = (by_name_arg_t *)indexes->arg; + + utils_read_lock(&pools_by_name_lock); + pool_name_dict_entry_t *entry = NULL; + // find pool name in the hashmap + HASH_FIND_STR(pools_by_name, name_arg->name, entry); + if (!entry) { + utils_read_unlock(&pools_by_name_lock); + LOG_ERR("Pool %s not found", name_arg->name); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + size_t count = 0; + pool_name_list_entry_t *it = NULL; + LL_COUNT(entry->pools, it, count); + // Special case: if user asked for umf.pool.by_name.name.count, we just return + // number of pools sharing the same name. + if (strcmp(extra_name, "count") == 0) { + if (name_arg->index != SIZE_MAX) { + LOG_ERR("count field requires no index argument"); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (queryType != CTL_QUERY_READ) { + LOG_ERR("count field is read only"); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + size_t *output = (size_t *)arg; + *output = count; + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_SUCCESS; + } + + if (queryType == CTL_QUERY_READ && count > 1 && + name_arg->index == SIZE_MAX) { + LOG_ERR("CTL 'by_name' read operation requires exactly one pool with " + "the specified name. " + "Actual number of pools with name '%s' is %zu. " + "You can add extra index parameter after the name to specify " + "exact pool e.g. umf.pool.by_name.pool_name,1.node_name", + name_arg->name, count); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + umf_result_t ret = UMF_RESULT_SUCCESS; + size_t nr = 0; + + if (name_arg->index != SIZE_MAX && name_arg->index >= count) { + LOG_ERR( + "Invalid index %zu. Actual number of pools with name '%s' is %zu. ", + name_arg->index, name_arg->name, count); + utils_read_unlock(&pools_by_name_lock); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + LL_FOREACH(entry->pools, it) { + if (name_arg->index != SIZE_MAX && nr++ != name_arg->index) { + continue; + } + va_list args2; + va_copy(args2, args); + umf_result_t r = ctl_query(&umf_pool_ctl_root, it->pool, source, + extra_name, queryType, arg, size, args2); + va_end(args2); + + if (r == UMF_RESULT_ERROR_INVALID_ARGUMENT) { + va_copy(args2, args); + r = it->pool->ops.ext_ctl(it->pool->pool_priv, source, extra_name, + arg, size, queryType, args2); + va_end(args2); + } + if (r != UMF_RESULT_SUCCESS && ret == UMF_RESULT_SUCCESS) { + ret = r; + } + } + utils_read_unlock(&pools_by_name_lock); + + return ret; +} + +static umf_ctl_node_t CTL_NODE(by_name)[] = { + CTL_LEAF_SUBTREE(CTL_NONAME, by_name), + CTL_NODE_END, +}; + umf_ctl_node_t CTL_NODE(pool)[] = {CTL_CHILD_WITH_ARG(by_handle), + CTL_CHILD_WITH_ARG(by_name), CTL_LEAF_SUBTREE(default), CTL_NODE_END}; -static void ctl_init(void) { +static void pool_ctl_init(void) { utils_mutex_init(&ctl_mtx); CTL_REGISTER_MODULE(&umf_pool_ctl_root, stats); } @@ -167,6 +456,13 @@ umfDefaultCtlPoolHandle(void *hPool, umf_ctl_query_source_t operationType, return UMF_RESULT_ERROR_NOT_SUPPORTED; } +static umf_result_t umfDefaultTrimMemory(void *provider, + size_t minBytesToKeep) { + (void)provider; + (void)minBytesToKeep; + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + // logical sum (OR) of all umf_pool_create_flags_t flags static const umf_pool_create_flags_t UMF_POOL_CREATE_FLAG_ALL = UMF_POOL_CREATE_FLAG_OWN_PROVIDER | UMF_POOL_CREATE_FLAG_DISABLE_TRACKING; @@ -174,12 +470,11 @@ static const umf_pool_create_flags_t UMF_POOL_CREATE_FLAG_ALL = // windows do not allow to use uninitialized va_list so this function help us to initialize it. static umf_result_t default_ctl_helper(const umf_memory_pool_ops_t *ops, void *ctl, const char *name, void *arg, - ...) { + size_t size, ...) { va_list empty_args; - va_start(empty_args, arg); - umf_result_t ret = - ops->ext_ctl(ctl, CTL_QUERY_PROGRAMMATIC, name, arg, UMF_DEFAULT_LEN, - CTL_QUERY_WRITE, empty_args); + va_start(empty_args, size); + umf_result_t ret = ops->ext_ctl(ctl, CTL_QUERY_PROGRAMMATIC, name, arg, + size, CTL_QUERY_WRITE, empty_args); va_end(empty_args); return ret; } @@ -189,9 +484,9 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, const void *params, umf_pool_create_flags_t flags, umf_memory_pool_handle_t *hPool) { - if (!ops || !provider || !hPool) { - return UMF_RESULT_ERROR_INVALID_ARGUMENT; - } + UMF_CHECK((ops != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((provider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); // validate flags if (flags & ~UMF_POOL_CREATE_FLAG_ALL) { @@ -201,10 +496,24 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, umf_result_t ret = UMF_RESULT_SUCCESS; + umf_memory_pool_ops_t compatible_ops; if (ops->version != UMF_POOL_OPS_VERSION_CURRENT) { LOG_WARN("Memory Pool ops version \"%d\" is different than the current " "version \"%d\"", ops->version, UMF_POOL_OPS_VERSION_CURRENT); + + // Create a new ops compatible structure with the current version + memset(&compatible_ops, 0, sizeof(compatible_ops)); + if (UMF_MINOR_VERSION(ops->version) == 0) { + LOG_INFO("Detected 1.0 version of Memory Pool ops, " + "upgrading to current version"); + memcpy(&compatible_ops, ops, + offsetof(umf_memory_pool_ops_t, ext_trim_memory)); + } else { + LOG_ERR("Unsupported Memory Pool ops version: %d", ops->version); + return UMF_RESULT_ERROR_NOT_SUPPORTED; + } + ops = &compatible_ops; } umf_memory_pool_handle_t pool = @@ -223,7 +532,7 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, pool->provider = provider; } - utils_init_once(&mem_pool_ctl_initialized, ctl_init); + utils_init_once(&mem_pool_ctl_initialized, pool_ctl_init); pool->flags = flags; pool->ops = *ops; @@ -234,6 +543,10 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, pool->ops.ext_ctl = umfDefaultCtlPoolHandle; } + if (NULL == pool->ops.ext_trim_memory) { + pool->ops.ext_trim_memory = umfDefaultTrimMemory; + } + if (NULL == utils_mutex_init(&pool->lock)) { LOG_ERR("Failed to initialize mutex for pool"); ret = UMF_RESULT_ERROR_UNKNOWN; @@ -246,22 +559,35 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, } // Set default property "name" to pool if exists - for (int i = 0; i < UMF_DEFAULT_SIZE; i++) { - const char *pname = NULL; - ret = ops->get_name(NULL, &pname); - if (ret != UMF_RESULT_SUCCESS) { - LOG_ERR("Failed to get pool name"); - goto err_pool_init; - } - if (CTL_DEFAULT_ENTRIES[i][0] != '\0' && pname && - strstr(CTL_DEFAULT_ENTRIES[i], pname)) { - - default_ctl_helper(ops, pool->pool_priv, CTL_DEFAULT_ENTRIES[i], - CTL_DEFAULT_VALUES[i]); + const char *pname = NULL; + ret = ops->get_name(NULL, &pname); + if (ret != UMF_RESULT_SUCCESS) { + LOG_ERR("Failed to get pool name"); + goto err_pool_init; + } + assert(pname != NULL); + + size_t pname_len = strlen(pname); + ctl_default_entry_t *it = NULL; + LL_FOREACH(ctl_default_list, it) { + if (strlen(it->name) > pname_len + 1 && + strncmp(it->name, pname, pname_len) == 0 && + it->name[pname_len] == '.') { + const char *ctl_name = it->name + pname_len + 1; + default_ctl_helper(ops, pool->pool_priv, ctl_name, it->value, + it->value_size); } } *hPool = pool; + pools_by_name_add(pool); + + const char *pool_name = NULL; + if (ops->get_name(pool->pool_priv, &pool_name) == UMF_RESULT_SUCCESS && + pool_name) { + utils_warn_invalid_name("Memory pool", pool_name); + } + LOG_INFO("Memory pool created: %p", (void *)pool); return UMF_RESULT_SUCCESS; @@ -277,10 +603,14 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops, } umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) { + UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + if (umf_ba_global_is_destroyed()) { return UMF_RESULT_ERROR_UNKNOWN; } + pools_by_name_remove(hPool); + umf_result_t ret = hPool->ops.finalize(hPool->pool_priv); umf_memory_provider_handle_t hUpstreamProvider = NULL; @@ -323,9 +653,18 @@ umf_result_t umfFree(void *ptr) { } umf_result_t umfPoolByPtr(const void *ptr, umf_memory_pool_handle_t *pool) { - UMF_CHECK((pool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); - *pool = umfMemoryTrackerGetPool(ptr); - return *pool ? UMF_RESULT_SUCCESS : UMF_RESULT_ERROR_INVALID_ARGUMENT; + UMF_CHECK(pool != NULL, UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK(ptr != NULL, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + umf_memory_properties_handle_t props = NULL; + umf_result_t ret = umfGetMemoryPropertiesHandle(ptr, &props); + if (ret != UMF_RESULT_SUCCESS || props == NULL || props->pool == NULL) { + *pool = NULL; + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *pool = props->pool; + return UMF_RESULT_SUCCESS; } umf_result_t umfPoolGetMemoryProvider(umf_memory_pool_handle_t hPool, @@ -454,3 +793,30 @@ umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) { utils_mutex_unlock(&hPool->lock); return UMF_RESULT_SUCCESS; } + +umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool, + size_t minBytesToKeep) { + UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + + return hPool->ops.ext_trim_memory(hPool->pool_priv, minBytesToKeep); +} + +void umfPoolCtlDefaultsDestroy(void) { + utils_init_once(&mem_pool_ctl_initialized, pool_ctl_init); + + utils_mutex_lock(&ctl_mtx); + + ctl_default_entry_t *entry = NULL, *tmp = NULL; + LL_FOREACH_SAFE(ctl_default_list, entry, tmp) { + LL_DELETE(ctl_default_list, entry); + if (entry->name) { + umf_ba_global_free(entry->name); + } + if (entry->value) { + umf_ba_global_free(entry->value); + } + umf_ba_global_free(entry); + } + + utils_mutex_unlock(&ctl_mtx); +} diff --git a/src/memory_pool_internal.h b/src/memory_pool_internal.h index 00f9a2a05..8c66cd4e7 100644 --- a/src/memory_pool_internal.h +++ b/src/memory_pool_internal.h @@ -47,6 +47,8 @@ typedef struct umf_memory_pool_t { extern umf_ctl_node_t CTL_NODE(pool)[]; +void umfPoolCtlDefaultsDestroy(void); + #ifdef __cplusplus } #endif diff --git a/src/memory_properties.c b/src/memory_properties.c new file mode 100644 index 000000000..11d4e5593 --- /dev/null +++ b/src/memory_properties.c @@ -0,0 +1,158 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#include + +#include +#include +#include + +#include "memory_properties_internal.h" +#include "memory_provider_internal.h" +#include "provider/provider_tracking.h" + +umf_result_t +umfGetMemoryPropertiesHandle(const void *ptr, + umf_memory_properties_handle_t *props_handle) { + UMF_CHECK((props_handle != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + + tracker_alloc_info_t *info = NULL; + umf_result_t ret = umfMemoryTrackerGetAllocInfo(ptr, &info); + if (ret == UMF_RESULT_SUCCESS) { + *props_handle = &info->props; + return UMF_RESULT_SUCCESS; + } + + // try to get IPC info + umf_ipc_info_t ipc_info; + ret = umfMemoryTrackerGetIpcInfo(ptr, &ipc_info); + if (ret == UMF_RESULT_SUCCESS) { + *props_handle = ipc_info.props; + return UMF_RESULT_SUCCESS; + } + + LOG_ERR("Failed to get memory properties handle for ptr=%p", ptr); + return ret; +} + +umf_result_t +umfGetMemoryPropertySize(umf_memory_properties_handle_t props_handle, + umf_memory_property_id_t memory_property_id, + size_t *size) { + UMF_CHECK((size != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_INVALID: + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + case UMF_MEMORY_PROPERTY_PROVIDER_HANDLE: + *size = sizeof(umf_memory_provider_handle_t); + return UMF_RESULT_SUCCESS; + case UMF_MEMORY_PROPERTY_POOL_HANDLE: + *size = sizeof(umf_memory_pool_handle_t); + return UMF_RESULT_SUCCESS; + case UMF_MEMORY_PROPERTY_BASE_ADDRESS: + *size = sizeof(uintptr_t); + return UMF_RESULT_SUCCESS; + case UMF_MEMORY_PROPERTY_BASE_SIZE: + *size = sizeof(size_t); + return UMF_RESULT_SUCCESS; + case UMF_MEMORY_PROPERTY_BUFFER_ID: + *size = sizeof(uint64_t); + return UMF_RESULT_SUCCESS; + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + *size = sizeof(umf_usm_memory_type_t); + return UMF_RESULT_SUCCESS; + default: + break; + } + + // custom memory properties should be handled by the user provider + umf_memory_provider_t *provider = props_handle->provider; + if (provider->ops.ext_get_allocation_properties_size) { + return provider->ops.ext_get_allocation_properties_size( + provider->provider_priv, memory_property_id, size); + } + + LOG_ERR("Unknown memory property ID: %d", memory_property_id); + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +umf_result_t umfGetMemoryProperty(umf_memory_properties_handle_t props_handle, + umf_memory_property_id_t memory_property_id, + void *value, size_t max_property_size) { + UMF_CHECK((value != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((props_handle != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((max_property_size > 0), UMF_RESULT_ERROR_INVALID_ARGUMENT); + + umf_memory_provider_t *provider = props_handle->provider; + + size_t property_size = 0; + umf_result_t ret = umfGetMemoryPropertySize( + props_handle, memory_property_id, &property_size); + if (UNLIKELY(ret != UMF_RESULT_SUCCESS)) { + LOG_ERR("Failed to get memory property size for ID %d", + memory_property_id); + return ret; + } + + if (UNLIKELY(property_size > max_property_size)) { + LOG_ERR("Memory property size %zu exceeds max size %zu for ID %d", + property_size, max_property_size, memory_property_id); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_INVALID: + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + + case UMF_MEMORY_PROPERTY_POOL_HANDLE: + *(umf_memory_pool_handle_t *)value = props_handle->pool; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_PROVIDER_HANDLE: + *(umf_memory_provider_handle_t *)value = provider; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_BUFFER_ID: + *(uint64_t *)value = props_handle->id; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_BASE_ADDRESS: + *(uintptr_t *)value = (uintptr_t)props_handle->base; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_BASE_SIZE: + *(size_t *)value = props_handle->base_size; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + // NOTE: this property is "cached" in the props_handle but the value is + // determined by the memory provider and set during addition to the + // tracker. + *(umf_usm_memory_type_t *)value = props_handle->memory_type; + return UMF_RESULT_SUCCESS; + + // GPU Memory Provider specific properties - should be handled by the + // provider + case UMF_MEMORY_PROPERTY_CONTEXT: + case UMF_MEMORY_PROPERTY_DEVICE: + default: + break; + }; + + // custom memory properties should be handled by the user provider + if (provider->ops.ext_get_allocation_properties) { + return provider->ops.ext_get_allocation_properties( + provider->provider_priv, props_handle->base, memory_property_id, + value); + } + + LOG_ERR("Unknown memory property ID: %d", memory_property_id); + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} diff --git a/src/memory_properties_internal.h b/src/memory_properties_internal.h new file mode 100644 index 000000000..3f2d6ef3a --- /dev/null +++ b/src/memory_properties_internal.h @@ -0,0 +1,45 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#ifndef UMF_MEMORY_PROPERTIES_INTERNAL_H +#define UMF_MEMORY_PROPERTIES_INTERNAL_H 1 + +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct umf_memory_properties_t { + umf_memory_pool_handle_t pool; + umf_memory_provider_handle_t provider; + uint64_t id; + void *base; + size_t base_size; + umf_usm_memory_type_t memory_type; +} umf_memory_properties_t; + +umf_result_t umfMemoryProviderGetAllocationProperties( + umf_memory_provider_handle_t hProvider, const void *ptr, + umf_memory_property_id_t propertyId, void *property_value); + +umf_result_t umfMemoryProviderGetAllocationPropertiesSize( + umf_memory_provider_handle_t hProvider, umf_memory_property_id_t propertyId, + size_t *size); + +#ifdef __cplusplus +} +#endif + +#endif // UMF_MEMORY_PROPERTIES_INTERNAL_H diff --git a/src/memory_provider.c b/src/memory_provider.c index 8ed2a79db..324fa751b 100644 --- a/src/memory_provider.c +++ b/src/memory_provider.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ #include "libumf.h" #include "memory_provider_internal.h" #include "utils_assert.h" +#include "utils_name.h" static umf_result_t CTL_SUBTREE_HANDLER(CTL_NONAME, by_handle)( void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, @@ -133,6 +135,25 @@ umfDefaultCtlHandle(void *provider, umf_ctl_query_source_t operationType, return UMF_RESULT_ERROR_NOT_SUPPORTED; } +static umf_result_t +umfDefaultGetAllocationProperties(void *provider, const void *ptr, + umf_memory_property_id_t propertyId, + void *propertyValue) { + (void)provider; + (void)ptr; + (void)propertyId; + (void)propertyValue; + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +static umf_result_t umfDefaultGetAllocationPropertiesSize( + void *provider, umf_memory_property_id_t propertyId, size_t *size) { + (void)provider; + (void)propertyId; + (void)size; + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + void assignOpsExtDefaults(umf_memory_provider_ops_t *ops) { if (!ops->ext_purge_lazy) { ops->ext_purge_lazy = umfDefaultPurgeLazy; @@ -153,6 +174,15 @@ void assignOpsExtDefaults(umf_memory_provider_ops_t *ops) { if (!ops->ext_ctl) { ops->ext_ctl = umfDefaultCtlHandle; } + + if (!ops->ext_get_allocation_properties) { + ops->ext_get_allocation_properties = umfDefaultGetAllocationProperties; + } + + if (!ops->ext_get_allocation_properties_size) { + ops->ext_get_allocation_properties_size = + umfDefaultGetAllocationPropertiesSize; + } } void assignOpsIpcDefaults(umf_memory_provider_ops_t *ops) { @@ -213,6 +243,14 @@ static bool validateOps(const umf_memory_provider_ops_t *ops) { return false; } + if ((ops->ext_get_allocation_properties == NULL) != + (ops->ext_get_allocation_properties_size == NULL)) { + LOG_ERR("ext_get_allocation_properties and " + "ext_get_allocation_properties_size must be " + "both set or both NULL\n"); + return false; + } + return true; } @@ -220,14 +258,36 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops, const void *params, umf_memory_provider_handle_t *hProvider) { libumfInit(); - if (!ops || !hProvider || !validateOps(ops)) { + if (!ops || !hProvider) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; } + umf_memory_provider_ops_t compatible_ops; if (ops->version != UMF_PROVIDER_OPS_VERSION_CURRENT) { LOG_WARN("Memory Provider ops version \"%d\" is different than the " "current version \"%d\"", ops->version, UMF_PROVIDER_OPS_VERSION_CURRENT); + + // Create a new ops compatible structure with the current version + memset(&compatible_ops, 0, sizeof(compatible_ops)); + + if (UMF_MINOR_VERSION(ops->version) == 0) { + LOG_INFO("Detected 1.0 version of Memory Provider ops, " + "upgrading to current version"); + memcpy(&compatible_ops, ops, + offsetof(umf_memory_provider_ops_t, + ext_get_allocation_properties)); + } else { + LOG_ERR("Unsupported Memory Provider ops version: %d", + ops->version); + return UMF_RESULT_ERROR_NOT_SUPPORTED; + } + + ops = &compatible_ops; + } + + if (!validateOps(ops)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; } umf_memory_provider_handle_t provider = @@ -252,6 +312,13 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops, *hProvider = provider; + const char *provider_name = NULL; + if (provider->ops.get_name(provider->provider_priv, &provider_name) == + UMF_RESULT_SUCCESS && + provider_name) { + utils_warn_invalid_name("Memory provider", provider_name); + } + return UMF_RESULT_SUCCESS; } @@ -499,3 +566,43 @@ umfMemoryProviderCloseIPCHandle(umf_memory_provider_handle_t hProvider, checkErrorAndSetLastProvider(res, hProvider); return res; } + +umf_result_t umfMemoryProviderGetAllocationProperties( + umf_memory_provider_handle_t hProvider, const void *ptr, + umf_memory_property_id_t propertyId, void *property_value) { + + UMF_CHECK((hProvider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((property_value != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((propertyId != UMF_MEMORY_PROPERTY_INVALID), + UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // NOTE: we do not check if the propertyId is below + // UMF_MEMORY_PROPERTY_MAX_RESERVED value, as the user could use a custom + // property ID that is above the reserved range + + umf_result_t res = hProvider->ops.ext_get_allocation_properties( + hProvider->provider_priv, ptr, propertyId, property_value); + + checkErrorAndSetLastProvider(res, hProvider); + return res; +} + +umf_result_t umfMemoryProviderGetAllocationPropertiesSize( + umf_memory_provider_handle_t hProvider, umf_memory_property_id_t propertyId, + size_t *size) { + + UMF_CHECK((hProvider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((size != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT); + UMF_CHECK((propertyId != UMF_MEMORY_PROPERTY_INVALID), + UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // NOTE: we do not check if the propertyId is below + // UMF_MEMORY_PROPERTY_MAX_RESERVED value, as the user could use a custom + // property ID that is above the reserved range + + umf_result_t res = hProvider->ops.ext_get_allocation_properties_size( + hProvider->provider_priv, propertyId, size); + + checkErrorAndSetLastProvider(res, hProvider); + return res; +} diff --git a/src/memspaces/memspace_highest_bandwidth.c b/src/memspaces/memspace_highest_bandwidth.c index 0790c406d..7bdc6f938 100644 --- a/src/memspaces/memspace_highest_bandwidth.c +++ b/src/memspaces/memspace_highest_bandwidth.c @@ -14,16 +14,15 @@ #include #include -// UMF_MEMSPACE_HIGHEST_BANDWIDTH requires HWLOC -// Additionally, it is currently unsupported on Win -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +// UMF_MEMSPACE_HIGHEST_BANDWIDTH is currently unsupported on Win +#if defined(_WIN32) umf_const_memspace_handle_t umfMemspaceHighestBandwidthGet(void) { // not supported return NULL; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "memspace_internal.h" @@ -119,4 +118,4 @@ umf_const_memspace_handle_t umfMemspaceHighestBandwidthGet(void) { return UMF_MEMSPACE_HIGHEST_BANDWIDTH; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#endif // !defined(_WIN32) diff --git a/src/memspaces/memspace_highest_capacity.c b/src/memspaces/memspace_highest_capacity.c index 36ef01b1c..31d4ced51 100644 --- a/src/memspaces/memspace_highest_capacity.c +++ b/src/memspaces/memspace_highest_capacity.c @@ -13,16 +13,15 @@ #include #include -// UMF_MEMSPACE_HIGHEST_CAPACITY requires HWLOC -// Additionally, it is currently unsupported on Win -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +// UMF_MEMSPACE_HIGHEST_CAPACITY is currently unsupported on Win +#if defined(_WIN32) umf_const_memspace_handle_t umfMemspaceHighestCapacityGet(void) { // not supported return NULL; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "memspace_internal.h" @@ -87,4 +86,4 @@ umf_const_memspace_handle_t umfMemspaceHighestCapacityGet(void) { return UMF_MEMSPACE_HIGHEST_CAPACITY; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#endif // !defined(_WIN32) diff --git a/src/memspaces/memspace_host_all.c b/src/memspaces/memspace_host_all.c index 06a07a660..5ff9e8edd 100644 --- a/src/memspaces/memspace_host_all.c +++ b/src/memspaces/memspace_host_all.c @@ -13,16 +13,14 @@ #include #include -// UMF_MEMSPACE_HOST_ALL requires HWLOC -// Additionally, it is currently unsupported on Win - -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +// UMF_MEMSPACE_HOST_ALL is currently unsupported on Win +#if defined(_WIN32) umf_const_memspace_handle_t umfMemspaceHostAllGet(void) { // not supported return NULL; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "memspace_internal.h" @@ -108,4 +106,4 @@ umf_const_memspace_handle_t umfMemspaceHostAllGet(void) { return UMF_MEMSPACE_HOST_ALL; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#endif // !defined(_WIN32) diff --git a/src/memspaces/memspace_lowest_latency.c b/src/memspaces/memspace_lowest_latency.c index 8fc33ae41..6e1780f2a 100644 --- a/src/memspaces/memspace_lowest_latency.c +++ b/src/memspaces/memspace_lowest_latency.c @@ -14,16 +14,15 @@ #include #include -// UMF_MEMSPACE_LOWEST_LATENCY requires HWLOC -// Additionally, it is currently unsupported on Win -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +// UMF_MEMSPACE_LOWEST_LATENCY is currently unsupported on Win +#if defined(_WIN32) umf_const_memspace_handle_t umfMemspaceLowestLatencyGet(void) { // not supported return NULL; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "memspace_internal.h" @@ -119,4 +118,4 @@ umf_const_memspace_handle_t umfMemspaceLowestLatencyGet(void) { return UMF_MEMSPACE_LOWEST_LATENCY; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#endif // !defined(_WIN32) diff --git a/src/memspaces/memspace_numa.c b/src/memspaces/memspace_numa.c index 4ac420ff7..421d50faf 100644 --- a/src/memspaces/memspace_numa.c +++ b/src/memspaces/memspace_numa.c @@ -12,9 +12,8 @@ #include #include -// umfMemspaceCreateFromNumaArray requires HWLOC -// Additionally, it is currently unsupported on Win -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +// umfMemspaceCreateFromNumaArray is currently unsupported on Win +#if defined(_WIN32) umf_result_t umfMemspaceCreateFromNumaArray(unsigned *nodeIds, size_t numIds, umf_memspace_handle_t *hMemspace) { (void)nodeIds; @@ -25,7 +24,7 @@ umf_result_t umfMemspaceCreateFromNumaArray(unsigned *nodeIds, size_t numIds, return UMF_RESULT_ERROR_NOT_SUPPORTED; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#else // !defined(_WIN32) #include "../memspace_internal.h" #include "../memtargets/memtarget_numa.h" @@ -76,4 +75,4 @@ umf_result_t umfMemspaceCreateFromNumaArray(unsigned *nodeIds, size_t numIds, return ret; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +#endif // !defined(_WIN32) diff --git a/src/pool/pool_disjoint.c b/src/pool/pool_disjoint.c index e5339376e..b9a37d98e 100644 --- a/src/pool/pool_disjoint.c +++ b/src/pool/pool_disjoint.c @@ -44,6 +44,7 @@ static umf_result_t CTL_READ_HANDLER(name)(void *ctx, disjoint_pool_t *pool = (disjoint_pool_t *)ctx; if (arg == NULL) { + LOG_ERR("arg is NULL"); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } @@ -64,6 +65,7 @@ static umf_result_t CTL_WRITE_HANDLER(name)(void *ctx, (void)source, (void)indexes, (void)size; disjoint_pool_t *pool = (disjoint_pool_t *)ctx; if (arg == NULL) { + LOG_ERR("arg is NULL"); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } @@ -81,6 +83,7 @@ CTL_READ_HANDLER(used_memory)(void *ctx, umf_ctl_query_source_t source, disjoint_pool_t *pool = (disjoint_pool_t *)ctx; if (arg == NULL || size != sizeof(size_t)) { + LOG_ERR("arg is NULL or size is not sizeof(size_t)"); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } @@ -119,6 +122,7 @@ CTL_READ_HANDLER(reserved_memory)(void *ctx, umf_ctl_query_source_t source, disjoint_pool_t *pool = (disjoint_pool_t *)ctx; if (arg == NULL || size != sizeof(size_t)) { + LOG_ERR("arg is NULL or size is not sizeof(size_t)"); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } @@ -148,12 +152,180 @@ CTL_READ_HANDLER(reserved_memory)(void *ctx, umf_ctl_query_source_t source, return UMF_RESULT_SUCCESS; } -static const umf_ctl_node_t CTL_NODE(stats)[] = {CTL_LEAF_RO(used_memory), - CTL_LEAF_RO(reserved_memory)}; +static umf_result_t CTL_READ_HANDLER(count)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + (void)source; + + disjoint_pool_t *pool = (disjoint_pool_t *)ctx; + if (arg == NULL || size != sizeof(size_t)) { + LOG_ERR("arg is NULL or size is not sizeof(size_t)"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (*(size_t *)indexes->arg != SIZE_MAX) { + LOG_ERR("to read buckets' count, you must call it without bucket id"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + assert(pool); + *(size_t *)arg = pool->buckets_num; + + return UMF_RESULT_SUCCESS; +} + +#define DEFINE_STATS_HANDLER(NAME, MEMBER) \ + static umf_result_t CTL_READ_HANDLER(NAME)( \ + void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, \ + umf_ctl_index_utlist_t *indexes) { \ + (void)source; \ + (void)indexes; \ + disjoint_pool_t *pool = (disjoint_pool_t *)ctx; \ + \ + if (arg == NULL || size != sizeof(size_t)) { \ + LOG_ERR("arg is NULL or size is not sizeof(size_t)"); \ + return UMF_RESULT_ERROR_INVALID_ARGUMENT; \ + } \ + \ + if (!pool->params.pool_trace) { \ + LOG_ERR("pool trace is disabled, cannot read " #NAME); \ + return UMF_RESULT_ERROR_NOT_SUPPORTED; \ + } \ + \ + size_t total = 0; \ + for (size_t i = 0; i < pool->buckets_num; ++i) { \ + bucket_t *bucket = pool->buckets[i]; \ + utils_mutex_lock(&bucket->bucket_lock); \ + total += bucket->MEMBER; \ + utils_mutex_unlock(&bucket->bucket_lock); \ + } \ + \ + *(size_t *)arg = total; \ + return UMF_RESULT_SUCCESS; \ + } + +DEFINE_STATS_HANDLER(alloc_num, alloc_count) +DEFINE_STATS_HANDLER(alloc_pool_num, alloc_pool_count) +DEFINE_STATS_HANDLER(free_num, free_count) +DEFINE_STATS_HANDLER(curr_slabs_in_use, curr_slabs_in_use) +DEFINE_STATS_HANDLER(curr_slabs_in_pool, curr_slabs_in_pool) +DEFINE_STATS_HANDLER(max_slabs_in_use, max_slabs_in_use) +DEFINE_STATS_HANDLER(max_slabs_in_pool, max_slabs_in_pool) + +static const umf_ctl_node_t CTL_NODE(stats)[] = { + CTL_LEAF_RO(used_memory), CTL_LEAF_RO(reserved_memory), + CTL_LEAF_RO(alloc_num), CTL_LEAF_RO(alloc_pool_num), + CTL_LEAF_RO(free_num), CTL_LEAF_RO(curr_slabs_in_use), + CTL_LEAF_RO(curr_slabs_in_pool), CTL_LEAF_RO(max_slabs_in_use), + CTL_LEAF_RO(max_slabs_in_pool), CTL_NODE_END, +}; + +#undef DEFINE_STATS_HANDLER + +#ifdef UMF_DEVELOPER_MODE +#define VALIDATE_BUCKETS_NAME(indexes) \ + if (strcmp("buckets", indexes->name) != 0) { \ + return UMF_RESULT_ERROR_INVALID_ARGUMENT; \ + } +#else +#define VALIDATE_BUCKETS_NAME(indexes) \ + do { \ + } while (0); +#endif + +#define DEFINE_BUCKET_STATS_HANDLER(NAME, MEMBER) \ + static umf_result_t CTL_READ_HANDLER(NAME, perBucket)( \ + void *ctx, umf_ctl_query_source_t source, void *arg, size_t size, \ + umf_ctl_index_utlist_t *indexes) { \ + (void)source; \ + \ + disjoint_pool_t *pool = (disjoint_pool_t *)ctx; \ + if (arg == NULL || size != sizeof(size_t)) { \ + LOG_ERR("arg is NULL or size is not sizeof(size_t)"); \ + return UMF_RESULT_ERROR_INVALID_ARGUMENT; \ + } \ + \ + VALIDATE_BUCKETS_NAME(indexes); \ + if (strcmp(#MEMBER, "size") != 0 && !pool->params.pool_trace) { \ + LOG_ERR("pool trace is disabled, cannot read " #NAME); \ + return UMF_RESULT_ERROR_NOT_SUPPORTED; \ + } \ + \ + size_t idx; \ + idx = *(size_t *)indexes->arg; \ + \ + if (idx >= pool->buckets_num) { \ + LOG_ERR("bucket id %zu is out of range [0, %zu)", idx, \ + pool->buckets_num); \ + return UMF_RESULT_ERROR_INVALID_ARGUMENT; \ + } \ + \ + bucket_t *bucket = pool->buckets[idx]; \ + utils_mutex_lock(&bucket->bucket_lock); \ + *(size_t *)arg = bucket->MEMBER; \ + utils_mutex_unlock(&bucket->bucket_lock); \ + \ + return UMF_RESULT_SUCCESS; \ + } + +DEFINE_BUCKET_STATS_HANDLER(alloc_num, alloc_count) +DEFINE_BUCKET_STATS_HANDLER(alloc_pool_num, alloc_pool_count) +DEFINE_BUCKET_STATS_HANDLER(free_num, free_count) +DEFINE_BUCKET_STATS_HANDLER(curr_slabs_in_use, curr_slabs_in_use) +DEFINE_BUCKET_STATS_HANDLER(curr_slabs_in_pool, curr_slabs_in_pool) +DEFINE_BUCKET_STATS_HANDLER(max_slabs_in_use, max_slabs_in_use) +DEFINE_BUCKET_STATS_HANDLER(max_slabs_in_pool, max_slabs_in_pool) + +static const umf_ctl_node_t CTL_NODE(stats, perBucket)[] = { + CTL_LEAF_RO(alloc_num, perBucket), + CTL_LEAF_RO(alloc_pool_num, perBucket), + CTL_LEAF_RO(free_num, perBucket), + CTL_LEAF_RO(curr_slabs_in_use, perBucket), + CTL_LEAF_RO(curr_slabs_in_pool, perBucket), + CTL_LEAF_RO(max_slabs_in_use, perBucket), + CTL_LEAF_RO(max_slabs_in_pool, perBucket), + CTL_NODE_END, +}; + +// Not a counter; but it is read exactly like other per-bucket stats, so we can use macro. +DEFINE_BUCKET_STATS_HANDLER(size, size) + +#undef DEFINE_BUCKET_STATS_HANDLER + +static const umf_ctl_node_t CTL_NODE(buckets)[] = { + CTL_LEAF_RO(count), CTL_LEAF_RO(size, perBucket), + CTL_CHILD(stats, perBucket), CTL_NODE_END}; + +static int bucket_id_parser(const void *arg, void *dest, size_t dest_size) { + size_t *out = (size_t *)dest; + assert(out); + + if (arg == NULL) { + *out = SIZE_MAX; + return 1; // node n + } + + int ret = ctl_arg_unsigned(arg, dest, dest_size); + if (ret) { + *out = SIZE_MAX; + return 1; + } + + return 0; +} + +static const struct ctl_argument CTL_ARG(buckets) = { + sizeof(size_t), + {{0, sizeof(size_t), CTL_ARG_TYPE_UNSIGNED_LONG_LONG, bucket_id_parser}, + CTL_ARG_PARSER_END}}; static void initialize_disjoint_ctl(void) { CTL_REGISTER_MODULE(&disjoint_ctl_root, stats); - // CTL_REGISTER_MODULE(&disjoint_ctl_root, name); + CTL_REGISTER_MODULE(&disjoint_ctl_root, buckets); + // TODO: this is hack. Need some way to register module as node with argument + disjoint_ctl_root.root[disjoint_ctl_root.first_free - 1].arg = + &CTL_ARG(buckets); } umf_result_t disjoint_pool_ctl(void *hPool, @@ -444,11 +616,11 @@ static void bucket_free_chunk(bucket_t *bucket, void *ptr, slab_t *slab, // remove slab slab_list_item_t *slab_it = &slab->iter; assert(slab_it->val != NULL); - pool_unregister_slab(bucket->pool, slab_it->val); + destroy_slab(slab_it->val); DL_DELETE(bucket->available_slabs, slab_it); assert(bucket->available_slabs_num > 0); bucket->available_slabs_num--; - destroy_slab(slab_it->val); + pool_unregister_slab(bucket->pool, slab_it->val); } } else { // return this chunk to the pool @@ -982,14 +1154,19 @@ umf_result_t disjoint_pool_malloc_usable_size(void *pool, const void *ptr, critnib_release(disjoint_pool->known_slabs, ref_slab); } - umf_alloc_info_t allocInfo = {NULL, 0, NULL}; - umf_result_t ret = umfMemoryTrackerGetAllocInfo(ptr, &allocInfo); - if (ret != UMF_RESULT_SUCCESS) { - *size = 0; - return ret; + umf_memory_properties_handle_t props = NULL; + umf_result_t umf_result = umfGetMemoryPropertiesHandle(ptr, &props); + if (umf_result != UMF_RESULT_SUCCESS) { + return umf_result; + } + + if (props == NULL) { + TLS_last_allocation_error = UMF_RESULT_ERROR_UNKNOWN; + LOG_ERR("failed to get allocation info from the memory tracker"); + return UMF_RESULT_ERROR_UNKNOWN; } - *size = allocInfo.baseSize; + *size = props->base_size; return UMF_RESULT_SUCCESS; } @@ -1025,15 +1202,21 @@ umf_result_t disjoint_pool_free(void *pool, void *ptr) { critnib_release(disjoint_pool->known_slabs, ref_slab); } - umf_alloc_info_t allocInfo = {NULL, 0, NULL}; - umf_result_t ret = umfMemoryTrackerGetAllocInfo(ptr, &allocInfo); + umf_memory_properties_handle_t props = NULL; + umf_result_t ret = umfGetMemoryPropertiesHandle(ptr, &props); if (ret != UMF_RESULT_SUCCESS) { TLS_last_allocation_error = ret; LOG_ERR("failed to get allocation info from the memory tracker"); return ret; } - size_t size = allocInfo.baseSize; + if (props == NULL) { + TLS_last_allocation_error = UMF_RESULT_ERROR_UNKNOWN; + LOG_ERR("failed to get allocation info from the memory tracker"); + return UMF_RESULT_ERROR_UNKNOWN; + } + + size_t size = props->base_size; umf_memory_provider_handle_t provider = disjoint_pool->provider; ret = umfMemoryProviderFree(provider, ptr, size); if (ret != UMF_RESULT_SUCCESS) { @@ -1133,6 +1316,47 @@ static umf_result_t disjoint_pool_get_name(void *pool, const char **name) { return UMF_RESULT_SUCCESS; } +umf_result_t disjoint_pool_trim_memory(void *pool, size_t minBytesToKeep) { + assert(pool != NULL); + disjoint_pool_t *hPool = (disjoint_pool_t *)pool; + + for (size_t i = 0; i < hPool->buckets_num; i++) { + bucket_t *bucket = hPool->buckets[i]; + utils_mutex_lock(&bucket->bucket_lock); + + // remove empty slabs from the pool + slab_list_item_t *it = NULL, *tmp = NULL; + LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) { + slab_t *slab = it->val; + if (slab->num_chunks_allocated == 0) { + if (minBytesToKeep > 0) { + // if we still have bytes to keep, do not remove slab + if (minBytesToKeep > slab->slab_size) { + minBytesToKeep -= slab->slab_size; + } else { + minBytesToKeep = 0; + } + continue; + } + + // remove slab + destroy_slab(slab); + DL_DELETE(bucket->available_slabs, it); + assert(bucket->available_slabs_num > 0); + bucket->available_slabs_num--; + pool_unregister_slab(hPool, slab); + + // update stats + bucket_update_stats(bucket, 0, -1); + } + } + + utils_mutex_unlock(&bucket->bucket_lock); + } + + return UMF_RESULT_SUCCESS; +} + static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = disjoint_pool_initialize, @@ -1146,6 +1370,7 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = { .get_last_allocation_error = disjoint_pool_get_last_allocation_error, .get_name = disjoint_pool_get_name, .ext_ctl = disjoint_pool_ctl, + .ext_trim_memory = disjoint_pool_trim_memory, }; const umf_memory_pool_ops_t *umfDisjointPoolOps(void) { diff --git a/src/pool/pool_jemalloc.c b/src/pool/pool_jemalloc.c index 343b30a28..6882e2dd6 100644 --- a/src/pool/pool_jemalloc.c +++ b/src/pool/pool_jemalloc.c @@ -46,19 +46,30 @@ umfJemallocPoolParamsSetNumArenas(umf_jemalloc_pool_params_handle_t hParams, return UMF_RESULT_ERROR_NOT_SUPPORTED; } +umf_result_t +umfJemallocPoolParamsSetName(umf_jemalloc_pool_params_handle_t hParams, + const char *name) { + (void)hParams; // unused + (void)name; // unused + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + #else #include #define MALLOCX_ARENA_MAX (MALLCTL_ARENAS_ALL - 1) +#define DEFAULT_NAME "jemalloc" typedef struct umf_jemalloc_pool_params_t { size_t n_arenas; + char name[64]; } umf_jemalloc_pool_params_t; typedef struct jemalloc_memory_pool_t { umf_memory_provider_handle_t provider; size_t n_arenas; + char name[64]; unsigned int arena_index[]; } jemalloc_memory_pool_t; @@ -117,7 +128,7 @@ static void *arena_extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, #ifndef __SANITIZE_ADDRESS__ // jemalloc might write to new extents in realloc, so we cannot - // mark them as unaccessible under asan + // mark them as inaccessible under asan utils_annotate_memory_inaccessible(ptr, size); #endif @@ -502,6 +513,13 @@ static umf_result_t op_initialize(umf_memory_provider_handle_t provider, if (!pool) { return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + memset(pool, 0, sizeof(*pool) + n_arenas * sizeof(*pool->arena_index)); + const char *pool_name = DEFAULT_NAME; + if (jemalloc_params) { + pool_name = jemalloc_params->name; + } + + snprintf(pool->name, sizeof(pool->name), "%s", pool_name); pool->provider = provider; pool->n_arenas = n_arenas; @@ -601,8 +619,37 @@ static umf_result_t op_get_last_allocation_error(void *pool) { } static umf_result_t op_get_name(void *pool, const char **name) { - (void)pool; - *name = "jemalloc"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (pool == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool; + *name = je_pool->name; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t op_trim_memory(void *pool, size_t minBytesToKeep) { + // there is no way to tell jemalloc to keep a minimum number of bytes + // so we just purge all arenas + if (minBytesToKeep > 0) { + LOG_WARN("Ignoring minBytesToKeep (%zu) in jemalloc pool", + minBytesToKeep); + } + + jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool; + for (size_t i = 0; i < je_pool->n_arenas; i++) { + char cmd[64]; + unsigned arena = je_pool->arena_index[i]; + snprintf(cmd, sizeof(cmd), "arena.%u.purge", arena); + if (je_mallctl(cmd, NULL, NULL, NULL, 0)) { + LOG_ERR("Could not purge jemalloc arena %u", arena); + return UMF_RESULT_ERROR_UNKNOWN; + } + } + return UMF_RESULT_SUCCESS; } @@ -618,6 +665,7 @@ static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = { .free = op_free, .get_last_allocation_error = op_get_last_allocation_error, .get_name = op_get_name, + .ext_trim_memory = op_trim_memory, }; const umf_memory_pool_ops_t *umfJemallocPoolOps(void) { @@ -631,6 +679,8 @@ umfJemallocPoolParamsCreate(umf_jemalloc_pool_params_handle_t *hParams) { return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } memset(params, 0, sizeof(*params)); + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; *hParams = params; return UMF_RESULT_SUCCESS; } @@ -652,4 +702,23 @@ umfJemallocPoolParamsSetNumArenas(umf_jemalloc_pool_params_handle_t hParams, return UMF_RESULT_SUCCESS; } +umf_result_t +umfJemallocPoolParamsSetName(umf_jemalloc_pool_params_handle_t hParams, + const char *name) { + if (!hParams) { + LOG_ERR("jemalloc pool params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (!name) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + #endif /* UMF_POOL_JEMALLOC_ENABLED */ diff --git a/src/pool/pool_proxy.c b/src/pool/pool_proxy.c index c6bf74124..6e256c491 100644 --- a/src/pool/pool_proxy.c +++ b/src/pool/pool_proxy.c @@ -100,11 +100,22 @@ static umf_result_t proxy_free(void *pool, void *ptr) { struct proxy_memory_pool *hPool = (struct proxy_memory_pool *)pool; if (ptr) { - umf_alloc_info_t allocInfo = {NULL, 0, NULL}; - umf_result_t umf_result = umfMemoryTrackerGetAllocInfo(ptr, &allocInfo); - if (umf_result == UMF_RESULT_SUCCESS) { - size = allocInfo.baseSize; + umf_memory_properties_handle_t props = NULL; + umf_result_t umf_result = umfGetMemoryPropertiesHandle(ptr, &props); + + if (umf_result != UMF_RESULT_SUCCESS) { + TLS_last_allocation_error = umf_result; + LOG_ERR("failed to get allocation info from the memory tracker"); + return umf_result; + } + + if (props == NULL) { + TLS_last_allocation_error = UMF_RESULT_ERROR_UNKNOWN; + LOG_ERR("failed to get allocation info from the memory tracker"); + return UMF_RESULT_ERROR_UNKNOWN; } + + size = props->base_size; } return umfMemoryProviderFree(hPool->hProvider, ptr, size); @@ -147,7 +158,9 @@ static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = { .malloc_usable_size = proxy_malloc_usable_size, .free = proxy_free, .get_last_allocation_error = proxy_get_last_allocation_error, - .get_name = proxy_get_name}; + .get_name = proxy_get_name, + .ext_trim_memory = NULL, // not supported +}; const umf_memory_pool_ops_t *umfProxyPoolOps(void) { return &UMF_PROXY_POOL_OPS; diff --git a/src/pool/pool_scalable.c b/src/pool/pool_scalable.c index 982a3408d..72afce267 100644 --- a/src/pool/pool_scalable.c +++ b/src/pool/pool_scalable.c @@ -36,6 +36,7 @@ static __TLS umf_result_t TLS_last_allocation_error; static __TLS umf_result_t TLS_last_free_error; static const size_t DEFAULT_GRANULARITY = 2 * 1024 * 1024; // 2MB +static const char *DEFAULT_NAME = "scalable"; typedef struct tbb_mem_pool_policy_t { raw_alloc_tbb_type pAlloc; @@ -48,6 +49,7 @@ typedef struct tbb_mem_pool_policy_t { typedef struct umf_scalable_pool_params_t { size_t granularity; bool keep_all_memory; + char name[64]; } umf_scalable_pool_params_t; typedef struct tbb_callbacks_t { @@ -70,6 +72,7 @@ typedef struct tbb_callbacks_t { typedef struct tbb_memory_pool_t { umf_memory_provider_handle_t mem_provider; void *tbb_pool; + char name[64]; } tbb_memory_pool_t; typedef enum tbb_enums_t { @@ -216,6 +219,8 @@ umfScalablePoolParamsCreate(umf_scalable_pool_params_handle_t *hParams) { params_data->granularity = DEFAULT_GRANULARITY; params_data->keep_all_memory = false; + strncpy(params_data->name, DEFAULT_NAME, sizeof(params_data->name) - 1); + params_data->name[sizeof(params_data->name) - 1] = '\0'; *hParams = (umf_scalable_pool_params_handle_t)params_data; @@ -265,6 +270,25 @@ umfScalablePoolParamsSetKeepAllMemory(umf_scalable_pool_params_handle_t hParams, return UMF_RESULT_SUCCESS; } +umf_result_t +umfScalablePoolParamsSetName(umf_scalable_pool_params_handle_t hParams, + const char *name) { + if (!hParams) { + LOG_ERR("scalable pool params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (!name) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + static umf_result_t tbb_pool_initialize(umf_memory_provider_handle_t provider, const void *params, void **pool) { tbb_mem_pool_policy_t policy = {.pAlloc = tbb_raw_alloc_wrapper, @@ -275,11 +299,13 @@ static umf_result_t tbb_pool_initialize(umf_memory_provider_handle_t provider, .keep_all_memory = false, .reserved = 0}; + const char *pool_name = DEFAULT_NAME; // If params is provided, override defaults if (params) { const umf_scalable_pool_params_t *scalable_params = params; policy.granularity = scalable_params->granularity; policy.keep_all_memory = scalable_params->keep_all_memory; + pool_name = scalable_params->name; } tbb_memory_pool_t *pool_data = @@ -288,6 +314,8 @@ static umf_result_t tbb_pool_initialize(umf_memory_provider_handle_t provider, LOG_ERR("cannot allocate memory for metadata"); return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + memset(pool_data, 0, sizeof(*pool_data)); + snprintf(pool_data->name, sizeof(pool_data->name), "%s", pool_name); umf_result_t res = UMF_RESULT_SUCCESS; int ret = init_tbb_callbacks(); @@ -422,19 +450,28 @@ static umf_result_t tbb_get_last_allocation_error(void *pool) { return TLS_last_allocation_error; } +static void initialize_pool_ctl(void) {} + static umf_result_t pool_ctl(void *hPool, umf_ctl_query_source_t operationType, const char *name, void *arg, size_t size, umf_ctl_query_type_t query_type, va_list args) { (void)operationType; // unused umf_memory_pool_handle_t pool_provider = (umf_memory_pool_handle_t)hPool; - utils_init_once(&ctl_initialized, NULL); + utils_init_once(&ctl_initialized, initialize_pool_ctl); return ctl_query(&pool_scallable_ctl_root, pool_provider->pool_priv, CTL_QUERY_PROGRAMMATIC, name, query_type, arg, size, args); } static umf_result_t scalable_get_name(void *pool, const char **name) { - (void)pool; // unused - *name = "scalable"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (pool == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + tbb_memory_pool_t *pool_data = (tbb_memory_pool_t *)pool; + *name = pool_data->name; return UMF_RESULT_SUCCESS; } @@ -451,6 +488,7 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = { .get_last_allocation_error = tbb_get_last_allocation_error, .ext_ctl = pool_ctl, .get_name = scalable_get_name, + .ext_trim_memory = NULL, // not supported }; const umf_memory_pool_ops_t *umfScalablePoolOps(void) { diff --git a/src/provider/provider_cuda.c b/src/provider/provider_cuda.c index 983be6b55..b6c15d2d1 100644 --- a/src/provider/provider_cuda.c +++ b/src/provider/provider_cuda.c @@ -12,6 +12,7 @@ #include #include +#include "memory_provider_internal.h" #include "provider_ctl_stats_type.h" #include "provider_cuda_internal.h" #include "utils_load_library.h" @@ -55,6 +56,7 @@ typedef struct cu_memory_provider_t { size_t min_alignment; unsigned int alloc_flags; ctl_stats_t stats; + char name[64]; } cu_memory_provider_t; #define CTL_PROVIDER_TYPE cu_memory_provider_t @@ -73,6 +75,7 @@ typedef struct umf_cuda_memory_provider_params_t { // Allocation flags for cuMemHostAlloc/cuMemAllocManaged unsigned int alloc_flags; + char name[64]; } umf_cuda_memory_provider_params_t; typedef struct cu_ops_t { @@ -112,6 +115,8 @@ static umf_result_t cu_memory_provider_free(void *provider, void *ptr, #define TLS_MSG_BUF_LEN 1024 +static const char *DEFAULT_NAME = "CUDA"; + typedef struct cu_last_native_error_t { CUresult native_error; char msg_buff[TLS_MSG_BUF_LEN]; @@ -248,6 +253,8 @@ umf_result_t umfCUDAMemoryProviderParamsCreate( params_data->memory_type = UMF_MEMORY_TYPE_UNKNOWN; params_data->alloc_flags = 0; + strncpy(params_data->name, DEFAULT_NAME, sizeof(params_data->name) - 1); + params_data->name[sizeof(params_data->name) - 1] = '\0'; *hParams = params_data; @@ -310,6 +317,24 @@ umf_result_t umfCUDAMemoryProviderParamsSetAllocFlags( return UMF_RESULT_SUCCESS; } +umf_result_t umfCUDAMemoryProviderParamsSetName( + umf_cuda_memory_provider_params_handle_t hParams, const char *name) { + if (!hParams) { + LOG_ERR("CUDA Memory Provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (!name) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + static umf_result_t cu_memory_provider_initialize(const void *params, void **provider) { if (params == NULL) { @@ -345,7 +370,10 @@ static umf_result_t cu_memory_provider_initialize(const void *params, if (!cu_provider) { return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } - memset(cu_provider, 0, sizeof(cu_memory_provider_t)); + + memset(cu_provider, 0, sizeof(*cu_provider)); + snprintf(cu_provider->name, sizeof(cu_provider->name), "%s", + cu_params->name); // CUDA alloc functions doesn't allow to provide user alignment - get the // minimum one from the driver @@ -620,8 +648,15 @@ cu_memory_provider_get_recommended_page_size(void *provider, size_t size, static umf_result_t cu_memory_provider_get_name(void *provider, const char **name) { - (void)provider; - *name = "CUDA"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + cu_memory_provider_t *cu_provider = (cu_memory_provider_t *)provider; + *name = cu_provider->name; return UMF_RESULT_SUCCESS; } @@ -712,6 +747,61 @@ static umf_result_t cu_ctl(void *provider, umf_ctl_query_source_t operationType, query_type, arg, size, args); } +static umf_result_t cu_memory_provider_get_allocation_properties( + void *provider, const void *ptr, + umf_memory_property_id_t memory_property_id, void *value) { + + // unused + (void)ptr; + + cu_memory_provider_t *cuda_provider = (cu_memory_provider_t *)provider; + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + *(umf_usm_memory_type_t *)value = cuda_provider->memory_type; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_CONTEXT: + *(CUcontext *)value = cuda_provider->context; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_DEVICE: + *(CUdevice *)value = cuda_provider->device; + return UMF_RESULT_SUCCESS; + + default: + break; + }; + + return UMF_RESULT_ERROR_INVALID_ARGUMENT; +} + +static umf_result_t cu_memory_provider_get_allocation_properties_size( + void *provider, umf_memory_property_id_t memory_property_id, size_t *size) { + + // unused + (void)provider; + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + *size = sizeof(umf_usm_memory_type_t); + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_CONTEXT: + *size = sizeof(CUcontext); + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_DEVICE: + *size = sizeof(CUdevice); + return UMF_RESULT_SUCCESS; + + default: + break; + }; + + return UMF_RESULT_ERROR_INVALID_ARGUMENT; +} + static umf_memory_provider_ops_t UMF_CUDA_MEMORY_PROVIDER_OPS = { .version = UMF_PROVIDER_OPS_VERSION_CURRENT, .initialize = cu_memory_provider_initialize, @@ -735,6 +825,10 @@ static umf_memory_provider_ops_t UMF_CUDA_MEMORY_PROVIDER_OPS = { .ext_open_ipc_handle = cu_memory_provider_open_ipc_handle, .ext_close_ipc_handle = cu_memory_provider_close_ipc_handle, .ext_ctl = cu_ctl, + .ext_get_allocation_properties = + cu_memory_provider_get_allocation_properties, + .ext_get_allocation_properties_size = + cu_memory_provider_get_allocation_properties_size, }; const umf_memory_provider_ops_t *umfCUDAMemoryProviderOps(void) { @@ -790,6 +884,14 @@ umf_result_t umfCUDAMemoryProviderParamsSetAllocFlags( return UMF_RESULT_ERROR_NOT_SUPPORTED; } +umf_result_t umfCUDAMemoryProviderParamsSetName( + umf_cuda_memory_provider_params_handle_t hParams, const char *name) { + (void)hParams; + (void)name; + LOG_ERR("CUDA provider is disabled (UMF_BUILD_CUDA_PROVIDER is OFF)!"); + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + const umf_memory_provider_ops_t *umfCUDAMemoryProviderOps(void) { // not supported LOG_ERR("CUDA provider is disabled (UMF_BUILD_CUDA_PROVIDER is OFF)!"); diff --git a/src/provider/provider_devdax_memory.c b/src/provider/provider_devdax_memory.c index c495e48ac..7ddf3c72a 100644 --- a/src/provider/provider_devdax_memory.c +++ b/src/provider/provider_devdax_memory.c @@ -19,7 +19,7 @@ #include "utils_log.h" -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +#if defined(_WIN32) const umf_memory_provider_ops_t *umfDevDaxMemoryProviderOps(void) { // not supported @@ -62,7 +62,15 @@ umf_result_t umfDevDaxMemoryProviderParamsSetProtection( return UMF_RESULT_ERROR_NOT_SUPPORTED; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +umf_result_t umfDevDaxMemoryProviderParamsSetName( + umf_devdax_memory_provider_params_handle_t hParams, const char *name) { + (void)hParams; + (void)name; + LOG_ERR("DevDax memory provider is disabled!"); + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "coarse.h" @@ -76,6 +84,8 @@ umf_result_t umfDevDaxMemoryProviderParamsSetProtection( #define TLS_MSG_BUF_LEN 1024 +static const char *DEFAULT_NAME = "DEVDAX"; + typedef struct devdax_memory_provider_t { char path[PATH_MAX]; // a path to the device DAX size_t size; // size of the file used for memory mapping @@ -85,6 +95,7 @@ typedef struct devdax_memory_provider_t { unsigned protection; // combination of OS-specific protection flags coarse_t *coarse; // coarse library handle ctl_stats_t stats; + char name[64]; } devdax_memory_provider_t; #define CTL_PROVIDER_TYPE devdax_memory_provider_t @@ -95,6 +106,7 @@ typedef struct umf_devdax_memory_provider_params_t { char *path; size_t size; unsigned protection; + char name[64]; } umf_devdax_memory_provider_params_t; typedef struct devdax_last_native_error_t { @@ -186,6 +198,8 @@ static umf_result_t devdax_initialize(const void *params, void **provider) { } memset(devdax_provider, 0, sizeof(*devdax_provider)); + snprintf(devdax_provider->name, sizeof(devdax_provider->name), "%s", + in_params->name); coarse_params_t coarse_params = {0}; coarse_params.provider = devdax_provider; @@ -381,8 +395,16 @@ static umf_result_t devdax_purge_force(void *provider, void *ptr, size_t size) { } static umf_result_t devdax_get_name(void *provider, const char **name) { - (void)provider; // unused - *name = "DEVDAX"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + devdax_memory_provider_t *devdax_provider = + (devdax_memory_provider_t *)provider; + *name = devdax_provider->name; return UMF_RESULT_SUCCESS; } @@ -614,6 +636,9 @@ umf_result_t umfDevDaxMemoryProviderParamsCreate( return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; + params->path = NULL; params->size = 0; params->protection = UMF_PROTECTION_READ | UMF_PROTECTION_WRITE; @@ -698,4 +723,22 @@ umf_result_t umfDevDaxMemoryProviderParamsSetProtection( return UMF_RESULT_SUCCESS; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +umf_result_t umfDevDaxMemoryProviderParamsSetName( + umf_devdax_memory_provider_params_handle_t hParams, const char *name) { + if (hParams == NULL) { + LOG_ERR("DevDax Memory Provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (name == NULL) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + +#endif // !defined(_WIN32) diff --git a/src/provider/provider_file_memory.c b/src/provider/provider_file_memory.c index 210b90cee..bff4034b2 100644 --- a/src/provider/provider_file_memory.c +++ b/src/provider/provider_file_memory.c @@ -20,7 +20,7 @@ #include "utils_log.h" -#if defined(_WIN32) || defined(UMF_NO_HWLOC) +#if defined(_WIN32) const umf_memory_provider_ops_t *umfFileMemoryProviderOps(void) { // not supported @@ -68,7 +68,15 @@ umf_result_t umfFileMemoryProviderParamsSetVisibility( return UMF_RESULT_ERROR_NOT_SUPPORTED; } -#else // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +umf_result_t umfFileMemoryProviderParamsSetName( + umf_file_memory_provider_params_handle_t hParams, const char *name) { + (void)hParams; + (void)name; + LOG_ERR("File memory provider is disabled!"); + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +#else // !defined(_WIN32) #include "base_alloc_global.h" #include "coarse.h" @@ -83,6 +91,8 @@ umf_result_t umfFileMemoryProviderParamsSetVisibility( #define TLS_MSG_BUF_LEN 1024 +static const char *DEFAULT_NAME = "FILE"; + typedef struct file_memory_provider_t { utils_mutex_t lock; // lock for file parameters (size and offsets) @@ -113,7 +123,9 @@ typedef struct file_memory_provider_t { critnib *fd_offset_map; coarse_t *coarse; // coarse library handle + ctl_stats_t stats; + char name[64]; } file_memory_provider_t; #define CTL_PROVIDER_TYPE file_memory_provider_t @@ -124,6 +136,7 @@ typedef struct umf_file_memory_provider_params_t { char *path; unsigned protection; umf_memory_visibility_t visibility; + char name[64]; } umf_file_memory_provider_params_t; typedef struct file_last_native_error_t { @@ -218,6 +231,8 @@ static umf_result_t file_initialize(const void *params, void **provider) { } memset(file_provider, 0, sizeof(*file_provider)); + snprintf(file_provider->name, sizeof(file_provider->name), "%s", + in_params->name); ret = file_translate_params(in_params, file_provider); if (ret != UMF_RESULT_SUCCESS) { @@ -649,8 +664,15 @@ static umf_result_t file_purge_force(void *provider, void *ptr, size_t size) { } static umf_result_t file_get_name(void *provider, const char **name) { - (void)provider; // unused - *name = "FILE"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + *name = file_provider->name; return UMF_RESULT_SUCCESS; } @@ -942,6 +964,8 @@ umf_result_t umfFileMemoryProviderParamsCreate( params->path = NULL; params->protection = UMF_PROTECTION_READ | UMF_PROTECTION_WRITE; params->visibility = UMF_MEM_MAP_PRIVATE; + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; umf_result_t res = umfFileMemoryProviderParamsSetPath(params, path); if (res != UMF_RESULT_SUCCESS) { @@ -1023,4 +1047,22 @@ umf_result_t umfFileMemoryProviderParamsSetVisibility( return UMF_RESULT_SUCCESS; } -#endif // !defined(_WIN32) && !defined(UMF_NO_HWLOC) +umf_result_t umfFileMemoryProviderParamsSetName( + umf_file_memory_provider_params_handle_t hParams, const char *name) { + if (hParams == NULL) { + LOG_ERR("File Memory Provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (name == NULL) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + +#endif // !defined(_WIN32) diff --git a/src/provider/provider_fixed_memory.c b/src/provider/provider_fixed_memory.c index 08fd3e7f6..d761a4024 100644 --- a/src/provider/provider_fixed_memory.c +++ b/src/provider/provider_fixed_memory.c @@ -28,17 +28,21 @@ #define TLS_MSG_BUF_LEN 1024 +static const char *DEFAULT_NAME = "FIXED"; + typedef struct fixed_memory_provider_t { void *base; // base address of memory size_t size; // size of the memory region coarse_t *coarse; // coarse library handle ctl_stats_t stats; + char name[64]; } fixed_memory_provider_t; // Fixed Memory provider settings struct typedef struct umf_fixed_memory_provider_params_t { void *ptr; size_t size; + char name[64]; } umf_fixed_memory_provider_params_t; typedef struct fixed_last_native_error_t { @@ -110,6 +114,8 @@ static umf_result_t fixed_initialize(const void *params, void **provider) { } memset(fixed_provider, 0, sizeof(*fixed_provider)); + snprintf(fixed_provider->name, sizeof(fixed_provider->name), "%s", + in_params->name); coarse_params_t coarse_params = {0}; coarse_params.provider = fixed_provider; @@ -251,8 +257,16 @@ static umf_result_t fixed_purge_force(void *provider, void *ptr, size_t size) { } static umf_result_t fixed_get_name(void *provider, const char **name) { - (void)provider; // unused - *name = "FIXED"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + fixed_memory_provider_t *fixed_provider = + (fixed_memory_provider_t *)provider; + *name = fixed_provider->name; return UMF_RESULT_SUCCESS; } @@ -333,6 +347,9 @@ umf_result_t umfFixedMemoryProviderParamsCreate( return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; + umf_result_t ret = umfFixedMemoryProviderParamsSetMemory(params, ptr, size); if (ret != UMF_RESULT_SUCCESS) { umf_ba_global_free(params); @@ -375,3 +392,21 @@ umf_result_t umfFixedMemoryProviderParamsSetMemory( hParams->size = size; return UMF_RESULT_SUCCESS; } + +umf_result_t umfFixedMemoryProviderParamsSetName( + umf_fixed_memory_provider_params_handle_t hParams, const char *name) { + if (hParams == NULL) { + LOG_ERR("Memory Provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (name == NULL) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} diff --git a/src/provider/provider_level_zero.c b/src/provider/provider_level_zero.c index d5e79b244..d5ab3e8e4 100644 --- a/src/provider/provider_level_zero.c +++ b/src/provider/provider_level_zero.c @@ -14,6 +14,7 @@ #include #include +#include "memory_provider_internal.h" #include "provider_ctl_stats_type.h" #include "provider_level_zero_internal.h" #include "utils_load_library.h" @@ -32,6 +33,7 @@ void fini_ze_global_state(void) { #include "base_alloc_global.h" #include "libumf.h" +#include "provider_level_zero_internal.h" #include "utils_assert.h" #include "utils_common.h" #include "utils_concurrency.h" @@ -57,6 +59,7 @@ typedef struct umf_level_zero_memory_provider_params_t { freePolicy; ///< Memory free policy uint32_t device_ordinal; + char name[64]; } umf_level_zero_memory_provider_params_t; typedef struct ze_memory_provider_t { @@ -74,6 +77,7 @@ typedef struct ze_memory_provider_t { size_t min_page_size; uint32_t device_ordinal; + char name[64]; ctl_stats_t stats; } ze_memory_provider_t; @@ -113,6 +117,7 @@ static ze_ops_t g_ze_ops; static UTIL_ONCE_FLAG ze_is_initialized = UTIL_ONCE_FLAG_INIT; static bool Init_ze_global_state_failed; static __TLS ze_result_t TLS_last_native_error; +static const char *DEFAULT_NAME = "LEVEL_ZERO"; static void store_last_native_error(int32_t native_error) { TLS_last_native_error = native_error; @@ -249,6 +254,8 @@ umf_result_t umfLevelZeroMemoryProviderParamsCreate( params->resident_device_count = 0; params->freePolicy = UMF_LEVEL_ZERO_MEMORY_PROVIDER_FREE_POLICY_DEFAULT; params->device_ordinal = 0; + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; *hParams = params; @@ -318,6 +325,24 @@ umf_result_t umfLevelZeroMemoryProviderParamsSetDeviceOrdinal( return UMF_RESULT_SUCCESS; } +umf_result_t umfLevelZeroMemoryProviderParamsSetName( + umf_level_zero_memory_provider_params_handle_t hParams, const char *name) { + if (!hParams) { + LOG_ERR("Level Zero memory provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (!name) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + umf_result_t umfLevelZeroMemoryProviderParamsSetResidentDevices( umf_level_zero_memory_provider_params_handle_t hParams, ze_device_handle_t *hDevices, uint32_t deviceCount) { @@ -565,6 +590,9 @@ static umf_result_t ze_memory_provider_initialize(const void *params, LOG_ERR("Cannot allocate memory for Level Zero Memory Provider"); return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + memset(ze_provider, 0, sizeof(*ze_provider)); + snprintf(ze_provider->name, sizeof(ze_provider->name), "%s", + ze_params->name); ze_provider->context = ze_params->level_zero_context_handle; ze_provider->device = ze_params->level_zero_device_handle; @@ -687,8 +715,15 @@ ze_memory_provider_get_recommended_page_size(void *provider, size_t size, static umf_result_t ze_memory_provider_get_name(void *provider, const char **name) { - (void)provider; - *name = "LEVEL_ZERO"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + ze_memory_provider_t *ze_provider = (ze_memory_provider_t *)provider; + *name = ze_provider->name; return UMF_RESULT_SUCCESS; } @@ -838,6 +873,63 @@ static umf_result_t ze_ctl(void *hProvider, query_type, arg, size, args); } +static umf_result_t ze_memory_provider_get_allocation_properties( + void *provider, const void *ptr, + umf_memory_property_id_t memory_property_id, void *value) { + + // unused + (void)ptr; + + struct ze_memory_provider_t *ze_provider = + (struct ze_memory_provider_t *)provider; + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + *(umf_usm_memory_type_t *)value = + ze2umf_memory_type(ze_provider->memory_type); + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_CONTEXT: + *(ze_context_handle_t *)value = ze_provider->context; + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_DEVICE: + *(ze_device_handle_t *)value = ze_provider->device; + return UMF_RESULT_SUCCESS; + + default: + break; + } + + return UMF_RESULT_ERROR_INVALID_ARGUMENT; +} + +static umf_result_t ze_memory_provider_get_allocation_properties_size( + void *provider, umf_memory_property_id_t memory_property_id, size_t *size) { + + // unused + (void)provider; + + switch (memory_property_id) { + case UMF_MEMORY_PROPERTY_POINTER_TYPE: + *size = sizeof(umf_usm_memory_type_t); + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_CONTEXT: + *size = sizeof(ze_context_handle_t); + return UMF_RESULT_SUCCESS; + + case UMF_MEMORY_PROPERTY_DEVICE: + *size = sizeof(ze_device_handle_t); + return UMF_RESULT_SUCCESS; + + default: + break; + } + + return UMF_RESULT_ERROR_INVALID_ARGUMENT; +} + static umf_memory_provider_ops_t UMF_LEVEL_ZERO_MEMORY_PROVIDER_OPS = { .version = UMF_PROVIDER_OPS_VERSION_CURRENT, .initialize = ze_memory_provider_initialize, @@ -858,6 +950,10 @@ static umf_memory_provider_ops_t UMF_LEVEL_ZERO_MEMORY_PROVIDER_OPS = { .ext_open_ipc_handle = ze_memory_provider_open_ipc_handle, .ext_close_ipc_handle = ze_memory_provider_close_ipc_handle, .ext_ctl = ze_ctl, + .ext_get_allocation_properties = + ze_memory_provider_get_allocation_properties, + .ext_get_allocation_properties_size = + ze_memory_provider_get_allocation_properties_size, }; const umf_memory_provider_ops_t *umfLevelZeroMemoryProviderOps(void) { @@ -939,6 +1035,13 @@ umf_result_t umfLevelZeroMemoryProviderParamsSetDeviceOrdinal( return UMF_RESULT_ERROR_NOT_SUPPORTED; } +umf_result_t umfLevelZeroMemoryProviderParamsSetName( + umf_level_zero_memory_provider_params_handle_t hParams, const char *name) { + (void)hParams; + (void)name; + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + const umf_memory_provider_ops_t *umfLevelZeroMemoryProviderOps(void) { // not supported LOG_ERR("L0 memory provider is disabled! (UMF_BUILD_LEVEL_ZERO_PROVIDER is " diff --git a/src/provider/provider_os_memory.c b/src/provider/provider_os_memory.c index e984d8ee8..6bbb36ad2 100644 --- a/src/provider/provider_os_memory.c +++ b/src/provider/provider_os_memory.c @@ -8,97 +8,24 @@ #include #include #include - #include #include #include #include + #include #include #include #include #include -#include "ctl/ctl_internal.h" -#include "utils_assert.h" -// OS Memory Provider requires HWLOC -#if defined(UMF_NO_HWLOC) - -const umf_memory_provider_ops_t *umfOsMemoryProviderOps(void) { return NULL; } - -umf_result_t umfOsMemoryProviderParamsCreate( - umf_os_memory_provider_params_handle_t *hParams) { - (void)hParams; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsDestroy( - umf_os_memory_provider_params_handle_t hParams) { - (void)hParams; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetProtection( - umf_os_memory_provider_params_handle_t hParams, unsigned protection) { - (void)hParams; - (void)protection; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetVisibility( - umf_os_memory_provider_params_handle_t hParams, - umf_memory_visibility_t visibility) { - (void)hParams; - (void)visibility; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetShmName( - umf_os_memory_provider_params_handle_t hParams, const char *shm_name) { - (void)hParams; - (void)shm_name; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetNumaList( - umf_os_memory_provider_params_handle_t hParams, unsigned *numa_list, - unsigned numa_list_len) { - (void)hParams; - (void)numa_list; - (void)numa_list_len; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetNumaMode( - umf_os_memory_provider_params_handle_t hParams, umf_numa_mode_t numa_mode) { - (void)hParams; - (void)numa_mode; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetPartSize( - umf_os_memory_provider_params_handle_t hParams, size_t part_size) { - (void)hParams; - (void)part_size; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -umf_result_t umfOsMemoryProviderParamsSetPartitions( - umf_os_memory_provider_params_handle_t hParams, - umf_numa_split_partition_t *partitions, unsigned partitions_len) { - (void)hParams; - (void)partitions; - (void)partitions_len; - return UMF_RESULT_ERROR_NOT_SUPPORTED; -} - -#else // !defined(UMF_NO_HWLOC) - #include "base_alloc_global.h" #include "critnib.h" +#include "ctl/ctl_internal.h" #include "libumf.h" #include "provider_os_memory_internal.h" #include "topology.h" +#include "utils_assert.h" #include "utils_common.h" #include "utils_concurrency.h" #include "utils_log.h" @@ -110,6 +37,8 @@ umf_result_t umfOsMemoryProviderParamsSetPartitions( #define TLS_MSG_BUF_LEN 1024 +static const char *DEFAULT_NAME = "OS"; + typedef struct umf_os_memory_provider_params_t { // Combination of 'umf_mem_protection_flags_t' flags unsigned protection; @@ -134,6 +63,7 @@ typedef struct umf_os_memory_provider_params_t { umf_numa_split_partition_t *partitions; /// len of the partitions array unsigned partitions_len; + char name[64]; } umf_os_memory_provider_params_t; typedef struct os_last_native_error_t { @@ -629,6 +559,8 @@ static umf_result_t os_initialize(const void *params, void **provider) { } memset(os_provider, 0, sizeof(*os_provider)); + snprintf(os_provider->name, sizeof(os_provider->name), "%s", + in_params->name); os_provider->topo = umfGetTopologyReduced(); if (!os_provider->topo) { @@ -636,6 +568,7 @@ static umf_result_t os_initialize(const void *params, void **provider) { 0); LOG_ERR("HWLOC topology discovery failed"); ret = UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; + goto err_free_os_provider; } os_provider->fd_offset_map = critnib_new(NULL, NULL); @@ -1221,8 +1154,15 @@ static umf_result_t os_purge_force(void *provider, void *ptr, size_t size) { } static umf_result_t os_get_name(void *provider, const char **name) { - (void)provider; // unused - *name = "OS"; + if (!name) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (provider == NULL) { + *name = DEFAULT_NAME; + return UMF_RESULT_SUCCESS; + } + os_memory_provider_t *os_provider = (os_memory_provider_t *)provider; + *name = os_provider->name; return UMF_RESULT_SUCCESS; } @@ -1403,7 +1343,8 @@ static umf_result_t os_open_ipc_handle(void *provider, void *providerIpcData, os_ipc_data->visibility, fd, os_ipc_data->fd_offset); if (*ptr == NULL) { os_store_last_native_error(UMF_OS_RESULT_ERROR_ALLOC_FAILED, errno); - LOG_PERR("memory mapping failed"); + LOG_PERR("memory mapping failed: %zu bytes at fd=%d, offset=%zu", + os_ipc_data->size, fd, os_ipc_data->fd_offset); ret = UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; } @@ -1492,6 +1433,8 @@ umf_result_t umfOsMemoryProviderParamsCreate( params->part_size = 0; params->partitions = NULL; params->partitions_len = 0; + strncpy(params->name, DEFAULT_NAME, sizeof(params->name) - 1); + params->name[sizeof(params->name) - 1] = '\0'; *hParams = params; @@ -1648,4 +1591,21 @@ umf_result_t umfOsMemoryProviderParamsSetPartitions( return UMF_RESULT_SUCCESS; } -#endif // !defined(UMF_NO_HWLOC) +umf_result_t +umfOsMemoryProviderParamsSetName(umf_os_memory_provider_params_handle_t hParams, + const char *name) { + if (hParams == NULL) { + LOG_ERR("OS memory provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (name == NULL) { + LOG_ERR("name is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + strncpy(hParams->name, name, sizeof(hParams->name) - 1); + hParams->name[sizeof(hParams->name) - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} diff --git a/src/provider/provider_os_memory_internal.h b/src/provider/provider_os_memory_internal.h index 4d2e8e217..3648d4a88 100644 --- a/src/provider/provider_os_memory_internal.h +++ b/src/provider/provider_os_memory_internal.h @@ -70,6 +70,8 @@ typedef struct os_memory_provider_t { hwloc_topology_t topo; + char name[64]; + ctl_stats_t stats; } os_memory_provider_t; diff --git a/src/provider/provider_tracking.c b/src/provider/provider_tracking.c index 386eef0ba..cf76d2be7 100644 --- a/src/provider/provider_tracking.c +++ b/src/provider/provider_tracking.c @@ -15,6 +15,7 @@ #include #include +#include #include #include "base_alloc_global.h" @@ -22,6 +23,8 @@ #include "ipc_cache.h" #include "ipc_internal.h" #include "memory_pool_internal.h" +#include "memory_properties_internal.h" +#include "memory_provider_internal.h" #include "provider_tracking.h" #include "utils_common.h" #include "utils_concurrency.h" @@ -32,6 +35,8 @@ uint64_t IPC_HANDLE_ID = 0; +uint64_t unique_alloc_id = 0; // requires atomic access + struct umf_memory_tracker_t { umf_ba_pool_t *alloc_info_allocator; // Multilevel maps are needed to support the case @@ -43,19 +48,8 @@ struct umf_memory_tracker_t { critnib *ipc_segments_map; }; -typedef struct tracker_alloc_info_t { - umf_memory_pool_handle_t pool; - size_t size; - // number of overlapping memory regions - // in the next level of map - // falling within the current range - size_t n_children; -#if !defined(NDEBUG) && defined(UMF_DEVELOPER_MODE) - uint64_t is_freed; -#endif -} tracker_alloc_info_t; - typedef struct tracker_ipc_info_t { + umf_memory_properties_t props; size_t size; umf_memory_provider_handle_t provider; ipc_opened_cache_value_t *ipc_cache_value; @@ -113,7 +107,8 @@ static tracker_alloc_info_t *get_most_nested_alloc_segment( continue; } - utils_atomic_load_acquire_u64((uint64_t *)&rvalue->size, &rsize); + utils_atomic_load_acquire_u64((uint64_t *)&rvalue->props.base_size, + &rsize); utils_atomic_load_acquire_size_t(&rvalue->n_children, &n_children); if (found && ((uintptr_t)ptr < rkey + rsize) && n_children) { if (level == MAX_LEVELS_OF_ALLOC_SEGMENT_MAP - 1) { @@ -192,8 +187,26 @@ umfMemoryTrackerAddAtLevel(umf_memory_tracker_handle_t hTracker, int level, return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } - value->pool = pool; - value->size = size; + // get provider + umf_memory_provider_handle_t provider = NULL; + umfPoolGetMemoryProvider(pool, &provider); + + // get memory type + umf_usm_memory_type_t memory_type = UMF_MEMORY_TYPE_UNKNOWN; + if (provider && provider->ops.ext_get_allocation_properties) { + provider->ops.ext_get_allocation_properties( + provider->provider_priv, ptr, UMF_MEMORY_PROPERTY_POINTER_TYPE, + &memory_type); + } + + memset(&value->props, 0, sizeof(umf_memory_properties_t)); + value->props.id = utils_atomic_increment_u64(&unique_alloc_id); + value->props.base = (void *)ptr; + value->props.base_size = size; + value->props.pool = pool; + value->props.provider = provider; + value->props.memory_type = memory_type; + value->n_children = 0; #if !defined(NDEBUG) && defined(UMF_DEVELOPER_MODE) value->is_freed = 0; @@ -214,8 +227,8 @@ umfMemoryTrackerAddAtLevel(umf_memory_tracker_handle_t hTracker, int level, "child #%zu added to memory region: tracker=%p, level=%i, " "pool=%p, ptr=%p, size=%zu", n_children, (void *)hTracker, level - 1, - (void *)parent_value->pool, (void *)parent_key, - parent_value->size); + (void *)parent_value->props.pool, (void *)parent_key, + parent_value->props.base_size); assert(ref_parent_value); critnib_release(hTracker->alloc_segments_map[level - 1], ref_parent_value); @@ -286,7 +299,8 @@ static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, assert(is_freed != 0xDEADBEEF); #endif - utils_atomic_load_acquire_u64((uint64_t *)&rvalue->size, &rsize); + utils_atomic_load_acquire_u64((uint64_t *)&rvalue->props.base_size, + &rsize); if ((uintptr_t)ptr < rkey + rsize) { if (level == MAX_LEVELS_OF_ALLOC_SEGMENT_MAP - 1) { @@ -300,8 +314,8 @@ static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, "cannot insert to the tracker value (pool=%p, ptr=%p, " "size=%zu) " "that exceeds the parent value (pool=%p, ptr=%p, size=%zu)", - (void *)pool, ptr, size, (void *)rvalue->pool, (void *)rkey, - (size_t)rsize); + (void *)pool, ptr, size, (void *)rvalue->props.pool, + (void *)rkey, (size_t)rsize); return UMF_RESULT_ERROR_INVALID_ARGUMENT; } parent_key = rkey; @@ -363,7 +377,8 @@ static umf_result_t umfMemoryTrackerRemove(umf_memory_tracker_handle_t hTracker, LOG_DEBUG("memory region removed: tracker=%p, level=%i, pool=%p, ptr=%p, " "size=%zu", - (void *)hTracker, level, (void *)value->pool, ptr, value->size); + (void *)hTracker, level, (void *)value->props.pool, ptr, + value->props.base_size); // release the reference to the value got from critnib_remove() assert(ref_value); @@ -375,8 +390,9 @@ static umf_result_t umfMemoryTrackerRemove(umf_memory_tracker_handle_t hTracker, LOG_DEBUG( "child #%zu removed from memory region: tracker=%p, level=%i, " "pool=%p, ptr=%p, size=%zu", - n_children, (void *)hTracker, level - 1, (void *)parent_value->pool, - (void *)parent_key, parent_value->size); + n_children, (void *)hTracker, level - 1, + (void *)parent_value->props.pool, (void *)parent_key, + parent_value->props.base_size); assert(ref_parent_value); assert(level >= 1); @@ -405,10 +421,26 @@ umfMemoryTrackerAddIpcSegment(umf_memory_tracker_handle_t hTracker, return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } + // get memory type + umf_usm_memory_type_t memory_type = UMF_MEMORY_TYPE_UNKNOWN; + if (provider && provider->ops.ext_get_allocation_properties) { + provider->ops.ext_get_allocation_properties( + provider->provider_priv, ptr, UMF_MEMORY_PROPERTY_POINTER_TYPE, + &memory_type); + } + value->size = size; value->provider = provider; value->ipc_cache_value = cache_entry; + memset(&value->props, 0, sizeof(umf_memory_properties_t)); + value->props.id = utils_atomic_increment_u64(&unique_alloc_id); + value->props.base = (void *)ptr; + value->props.base_size = size; + value->props.pool = NULL; // unknown + value->props.provider = provider; + value->props.memory_type = memory_type; + int ret = critnib_insert(hTracker->ipc_segments_map, (uintptr_t)ptr, value, 0); if (ret == 0) { @@ -458,37 +490,26 @@ umfMemoryTrackerRemoveIpcSegment(umf_memory_tracker_handle_t hTracker, return UMF_RESULT_SUCCESS; } -umf_memory_pool_handle_t umfMemoryTrackerGetPool(const void *ptr) { - umf_alloc_info_t allocInfo = {NULL, 0, NULL}; - umf_result_t ret = umfMemoryTrackerGetAllocInfo(ptr, &allocInfo); - if (ret != UMF_RESULT_SUCCESS) { - return NULL; - } - - return allocInfo.pool; -} - umf_result_t umfMemoryTrackerGetAllocInfo(const void *ptr, - umf_alloc_info_t *pAllocInfo) { - assert(pAllocInfo); + tracker_alloc_info_t **info) { + assert(info); - if (ptr == NULL) { + if (UNLIKELY(ptr == NULL)) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; } - if (TRACKER == NULL) { + if (UNLIKELY(TRACKER == NULL)) { LOG_ERR("tracker does not exist"); return UMF_RESULT_ERROR_NOT_SUPPORTED; } - if (TRACKER->alloc_segments_map[0] == NULL) { + if (UNLIKELY(TRACKER->alloc_segments_map[0] == NULL)) { LOG_ERR("tracker's alloc_segments_map does not exist"); return UMF_RESULT_ERROR_NOT_SUPPORTED; } tracker_alloc_info_t *top_most_value = NULL; tracker_alloc_info_t *rvalue = NULL; - uintptr_t top_most_key = 0; uintptr_t rkey = 0; uint64_t rsize = 0; size_t n_children = 0; @@ -514,7 +535,6 @@ umf_result_t umfMemoryTrackerGetAllocInfo(const void *ptr, critnib_release(TRACKER->alloc_segments_map[level], ref_value); } top_most_value = NULL; - top_most_key = 0; rkey = 0; rsize = 0; level = 0; @@ -525,10 +545,10 @@ umf_result_t umfMemoryTrackerGetAllocInfo(const void *ptr, continue; } - utils_atomic_load_acquire_u64((uint64_t *)&rvalue->size, &rsize); + utils_atomic_load_acquire_u64((uint64_t *)&rvalue->props.base_size, + &rsize); utils_atomic_load_acquire_size_t(&rvalue->n_children, &n_children); if (found && (uintptr_t)ptr < rkey + rsize) { - top_most_key = rkey; top_most_value = rvalue; if (ref_top_most_value) { assert(level >= 1); @@ -555,9 +575,7 @@ umf_result_t umfMemoryTrackerGetAllocInfo(const void *ptr, return UMF_RESULT_ERROR_INVALID_ARGUMENT; } - pAllocInfo->base = (void *)top_most_key; - pAllocInfo->baseSize = top_most_value->size; - pAllocInfo->pool = top_most_value->pool; + *info = top_most_value; assert(ref_top_most_value); critnib_release(TRACKER->alloc_segments_map[ref_level], ref_top_most_value); @@ -603,6 +621,8 @@ umf_result_t umfMemoryTrackerGetIpcInfo(const void *ptr, pIpcInfo->baseSize = rvalue->size; pIpcInfo->provider = rvalue->provider; + pIpcInfo->props = &rvalue->props; + if (ref_value) { critnib_release(TRACKER->ipc_segments_map, ref_value); } @@ -692,9 +712,9 @@ static umf_result_t trackingAllocationSplit(void *hProvider, void *ptr, ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; goto err; } - if (value->size != totalSize) { + if (value->props.base_size != totalSize) { LOG_ERR("tracked size=%zu does not match requested size to split: %zu", - value->size, totalSize); + value->props.base_size, totalSize); ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; goto err; } @@ -732,7 +752,8 @@ static umf_result_t trackingAllocationSplit(void *hProvider, void *ptr, } // update the size of the first part - utils_atomic_store_release_u64((uint64_t *)&value->size, firstSize); + utils_atomic_store_release_u64((uint64_t *)&value->props.base_size, + firstSize); critnib_release(provider->hTracker->alloc_segments_map[level], ref_value); utils_mutex_unlock(&provider->hTracker->splitMergeMutex); @@ -805,12 +826,12 @@ static umf_result_t trackingAllocationMerge(void *hProvider, void *lowPtr, ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; goto err_fatal; } - if (lowValue->pool != highValue->pool) { + if (lowValue->props.pool != highValue->props.pool) { LOG_FATAL("pool mismatch"); ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; goto err_fatal; } - if (lowValue->size + highValue->size != totalSize) { + if (lowValue->props.base_size + highValue->props.base_size != totalSize) { LOG_FATAL("lowValue->size + highValue->size != totalSize"); ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; goto err_fatal; @@ -824,7 +845,8 @@ static umf_result_t trackingAllocationMerge(void *hProvider, void *lowPtr, } // we only need to update the size of the first part - utils_atomic_store_release_u64((uint64_t *)&lowValue->size, totalSize); + utils_atomic_store_release_u64((uint64_t *)&lowValue->props.base_size, + totalSize); size_t low_children = lowValue->n_children; size_t high_children = highValue->n_children; @@ -950,12 +972,13 @@ static void check_if_tracker_is_empty(umf_memory_tracker_handle_t hTracker, while (1 == critnib_find(hTracker->alloc_segments_map[i], last_key, FIND_G, &rkey, (void **)&rvalue, &ref_value)) { - if (rvalue && ((rvalue->pool == pool) || pool == NULL)) { + if (rvalue && ((rvalue->props.pool == pool) || pool == NULL)) { n_items++; LOG_DEBUG( "found abandoned allocation in the tracking provider: " "pool=%p, ptr=%p, size=%zu", - (void *)rvalue->pool, (void *)rkey, (size_t)rvalue->size); + (void *)rvalue->props.pool, (void *)rkey, + (size_t)rvalue->props.base_size); } if (ref_value) { @@ -1295,6 +1318,24 @@ static umf_result_t trackingCloseIpcHandle(void *provider, void *ptr, return umf_result; } +static umf_result_t +trackingGetAllocationProperties(void *provider, const void *ptr, + umf_memory_property_id_t memory_property_id, + void *value) { + umf_tracking_memory_provider_t *p = + (umf_tracking_memory_provider_t *)provider; + return umfMemoryProviderGetAllocationProperties(p->hUpstream, ptr, + memory_property_id, value); +} + +static umf_result_t trackingGetAllocationPropertiesSize( + void *provider, umf_memory_property_id_t memory_property_id, size_t *size) { + umf_tracking_memory_provider_t *p = + (umf_tracking_memory_provider_t *)provider; + return umfMemoryProviderGetAllocationPropertiesSize( + p->hUpstream, memory_property_id, size); +} + umf_memory_provider_ops_t UMF_TRACKING_MEMORY_PROVIDER_OPS = { .version = UMF_PROVIDER_OPS_VERSION_CURRENT, .initialize = trackingInitialize, @@ -1313,7 +1354,11 @@ umf_memory_provider_ops_t UMF_TRACKING_MEMORY_PROVIDER_OPS = { .ext_get_ipc_handle = trackingGetIpcHandle, .ext_put_ipc_handle = trackingPutIpcHandle, .ext_open_ipc_handle = trackingOpenIpcHandle, - .ext_close_ipc_handle = trackingCloseIpcHandle}; + .ext_close_ipc_handle = trackingCloseIpcHandle, + .ext_ctl = NULL, + .ext_get_allocation_properties = trackingGetAllocationProperties, + .ext_get_allocation_properties_size = trackingGetAllocationPropertiesSize, +}; static void free_ipc_cache_value(void *unused, void *ipc_cache_value) { (void)unused; diff --git a/src/provider/provider_tracking.h b/src/provider/provider_tracking.h index d7ee06c1b..cdbee3973 100644 --- a/src/provider/provider_tracking.h +++ b/src/provider/provider_tracking.h @@ -20,6 +20,7 @@ #include "base_alloc.h" #include "critnib.h" +#include "memory_properties_internal.h" #include "utils_concurrency.h" #ifdef __cplusplus @@ -34,18 +35,23 @@ extern umf_memory_tracker_handle_t TRACKER; umf_result_t umfMemoryTrackerCreate(umf_memory_tracker_handle_t *handle); void umfMemoryTrackerDestroy(umf_memory_tracker_handle_t handle); -umf_memory_pool_handle_t umfMemoryTrackerGetPool(const void *ptr); +typedef struct tracker_alloc_info_t { + umf_memory_properties_t props; -typedef struct umf_alloc_info_t { - void *base; - size_t baseSize; - umf_memory_pool_handle_t pool; -} umf_alloc_info_t; + // number of overlapping memory regions in the next level of map falling + // within the current range + size_t n_children; +#if !defined(NDEBUG) && defined(UMF_DEVELOPER_MODE) + uint64_t is_freed; +#endif +} tracker_alloc_info_t; umf_result_t umfMemoryTrackerGetAllocInfo(const void *ptr, - umf_alloc_info_t *pAllocInfo); + tracker_alloc_info_t **info); typedef struct umf_ipc_info_t { + umf_memory_properties_handle_t props; + void *base; size_t baseSize; umf_memory_provider_handle_t provider; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 7125d2603..e26017e79 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -5,7 +5,8 @@ include(${UMF_CMAKE_SOURCE_DIR}/cmake/helpers.cmake) include(FindThreads) -set(UMF_UTILS_SOURCES_COMMON utils_common.c utils_log.c utils_load_library.c) +set(UMF_UTILS_SOURCES_COMMON utils_common.c utils_log.c utils_load_library.c + ../ctl/ctl.c) set(UMF_UTILS_SOURCES_POSIX utils_posix_common.c utils_posix_concurrency.c) set(UMF_UTILS_SOURCES_LINUX utils_linux_common.c) set(UMF_UTILS_SOURCES_MACOSX utils_macosx_common.c) diff --git a/src/utils/utils_log.c b/src/utils/utils_log.c index 960ae4686..3c57ce033 100644 --- a/src/utils/utils_log.c +++ b/src/utils/utils_log.c @@ -10,7 +10,6 @@ #ifdef _WIN32 #include #else -#define _GNU_SOURCE 1 #include #include #include @@ -28,6 +27,7 @@ #include +#include "ctl/ctl_internal.h" #include "utils_assert.h" #include "utils_common.h" #include "utils_log.h" @@ -61,14 +61,16 @@ char const __umf_str_1__all_cmake_vars[] = #define MAX_ENV_LEN 2048 typedef struct { - int timestamp; - int pid; + bool enableTimestamp; + bool enablePid; utils_log_level_t level; utils_log_level_t flushLevel; FILE *output; + const char *file_name; } utils_log_config_t; -utils_log_config_t loggerConfig = {0, 0, LOG_ERROR, LOG_ERROR, NULL}; +utils_log_config_t loggerConfig = {false, false, LOG_ERROR, + LOG_ERROR, NULL, NULL}; static const char *level_to_str(utils_log_level_t l) { switch (l) { @@ -138,28 +140,29 @@ static void utils_log_internal(utils_log_level_t level, int perror, *err = '\0'; postfix = "[strerror_s failed]"; } -#elif defined(__APPLE__) - char err[1024]; // max size according to manpage. +#else int saveno = errno; errno = 0; - if (strerror_r(saveno, err, sizeof(err))) { - /* should never happen */ + +#if defined(__APPLE__) || \ + ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) + char err[1024]; + int ret = strerror_r(saveno, err, sizeof(err)); + if (ret) { *err = '\0'; postfix = "[strerror_r failed]"; } - if (errno == ERANGE) { postfix = "[truncated...]"; } - errno = saveno; #else - char err_buff[1024]; // max size according to manpage. - int saveno = errno; - errno = 0; + char err_buff[1024]; const char *err = strerror_r(saveno, err_buff, sizeof(err_buff)); if (errno == ERANGE) { postfix = "[truncated...]"; } +#endif + errno = saveno; #endif strncpy(b_pos, err, b_size); @@ -185,7 +188,7 @@ static void utils_log_internal(utils_log_level_t level, int perror, int h_size = sizeof(header); memset(header, 0, sizeof(header)); - if (loggerConfig.timestamp) { + if (loggerConfig.enableTimestamp) { time_t now = time(NULL); struct tm tm_info; #ifdef _WIN32 @@ -200,7 +203,7 @@ static void utils_log_internal(utils_log_level_t level, int perror, h_size -= tmp; } - if (loggerConfig.pid) { + if (loggerConfig.enablePid) { ASSERT(h_size > 0); tmp = snprintf(h_pos, h_size, "PID:%-6lu TID:%-6lu ", (unsigned long)pid, (unsigned long)tid); @@ -254,8 +257,10 @@ void utils_log_init(void) { const char *arg; if (utils_parse_var(envVar, "output:stdout", NULL)) { loggerConfig.output = stdout; + loggerConfig.file_name = "stdout"; } else if (utils_parse_var(envVar, "output:stderr", NULL)) { loggerConfig.output = stderr; + loggerConfig.file_name = "stderr"; } else if (utils_parse_var(envVar, "output:file", &arg)) { loggerConfig.output = NULL; const char *argEnd = strstr(arg, ";"); @@ -284,6 +289,7 @@ void utils_log_init(void) { loggerConfig.output = NULL; return; } + loggerConfig.file_name = file; } else { loggerConfig.output = stderr; LOG_ERR("Logging output not set - logging disabled (UMF_LOG = \"%s\")", @@ -293,15 +299,15 @@ void utils_log_init(void) { } if (utils_parse_var(envVar, "timestamp:yes", NULL)) { - loggerConfig.timestamp = 1; + loggerConfig.enableTimestamp = 1; } else if (utils_parse_var(envVar, "timestamp:no", NULL)) { - loggerConfig.timestamp = 0; + loggerConfig.enableTimestamp = 0; } if (utils_parse_var(envVar, "pid:yes", NULL)) { - loggerConfig.pid = 1; + loggerConfig.enablePid = 1; } else if (utils_parse_var(envVar, "pid:no", NULL)) { - loggerConfig.pid = 0; + loggerConfig.enablePid = 0; } if (utils_parse_var(envVar, "level:debug", NULL)) { @@ -328,9 +334,256 @@ void utils_log_init(void) { loggerConfig.flushLevel = LOG_FATAL; } - LOG_INFO( - "Logger enabled (" LOG_STR_UMF_VERSION - "level: %s, flush: %s, pid: %s, timestamp: %s)", - level_to_str(loggerConfig.level), level_to_str(loggerConfig.flushLevel), - bool_to_str(loggerConfig.pid), bool_to_str(loggerConfig.timestamp)); + LOG_INFO("Logger enabled (" LOG_STR_UMF_VERSION + "level: %s, flush: %s, pid: %s, timestamp: %s)", + level_to_str(loggerConfig.level), + level_to_str(loggerConfig.flushLevel), + bool_to_str(loggerConfig.enablePid), + bool_to_str(loggerConfig.enableTimestamp)); +} + +// this is needed for logger unit test +#ifndef DISABLE_CTL_LOGGER +static umf_result_t +CTL_READ_HANDLER(timestamp)(void *ctx, umf_ctl_query_source_t source, void *arg, + size_t size, umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + bool *arg_out = (bool *)arg; + + if (arg_out == NULL || size < sizeof(bool)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *arg_out = loggerConfig.enableTimestamp; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t +CTL_WRITE_HANDLER(timestamp)(void *ctx, umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + bool arg_in = *(bool *)arg; + + if (size < sizeof(bool)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + loggerConfig.enableTimestamp = arg_in; + LOG_INFO("Logger print timestamp set to %s", + bool_to_str(loggerConfig.enableTimestamp)); + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_READ_HANDLER(pid)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + bool *arg_out = (bool *)arg; + + if (arg_out == NULL || size < sizeof(bool)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *arg_out = loggerConfig.enablePid; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_WRITE_HANDLER(pid)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + bool arg_in = *(bool *)arg; + + if (size < sizeof(bool)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + loggerConfig.enablePid = arg_in; + LOG_INFO("Logger print pid %s set to", bool_to_str(loggerConfig.enablePid)); + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_READ_HANDLER(level)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + bool *arg_out = (bool *)arg; + + if (arg_out == NULL || size < sizeof(utils_log_level_t)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *arg_out = loggerConfig.level; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_WRITE_HANDLER(level)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + utils_log_level_t *arg_in = (utils_log_level_t *)arg; + + if (arg_in == NULL || *arg_in < LOG_DEBUG || *arg_in > LOG_FATAL || + size < sizeof(int)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + utils_log_level_t old = loggerConfig.level; + + // if new log level is higher then LOG_INFO print log before changing log level + // so if user changes from LOG_INFO to higher log level, it will get information about change anyway + if (*arg_in > LOG_INFO) { + LOG_INFO("Logger level changed from %s to %s", level_to_str(old), + level_to_str(*arg_in)); + loggerConfig.level = *arg_in; + } else { + loggerConfig.level = *arg_in; + LOG_INFO("Logger level changed from %s to %s", level_to_str(old), + level_to_str(loggerConfig.level)); + } + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t +CTL_READ_HANDLER(flush_level)(void *ctx, umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + utils_log_level_t *arg_out = (utils_log_level_t *)arg; + + if (arg_out == NULL || size < sizeof(utils_log_level_t)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *arg_out = loggerConfig.flushLevel; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t +CTL_WRITE_HANDLER(flush_level)(void *ctx, umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + utils_log_level_t *arg_in = (utils_log_level_t *)arg; + + if (arg_in == NULL || *arg_in < LOG_DEBUG || *arg_in > LOG_FATAL || + size < sizeof(int)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + loggerConfig.flushLevel = *arg_in; + LOG_INFO("Logger flush level set to %s", + level_to_str(loggerConfig.flushLevel)); + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_READ_HANDLER(output)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + const char **arg_out = (const char **)arg; + if (arg_out == NULL || size < sizeof(const char *)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (loggerConfig.output == NULL) { + *arg_out = "disabled"; + return UMF_RESULT_SUCCESS; + } + + *arg_out = loggerConfig.file_name; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t CTL_WRITE_HANDLER(output)(void *ctx, + umf_ctl_query_source_t source, + void *arg, size_t size, + umf_ctl_index_utlist_t *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + const char *arg_in = *(const char **)arg; + if (size < sizeof(const char *)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + FILE *oldHandle = loggerConfig.output; + const char *oldName = + loggerConfig.file_name ? loggerConfig.file_name : "disabled"; + + if (arg_in == NULL) { + if (loggerConfig.output) { + LOG_INFO("Logger disabled"); + if (oldHandle != stdout && oldHandle != stderr) { + fclose(oldHandle); + } + loggerConfig.output = NULL; + loggerConfig.file_name = NULL; + } + return UMF_RESULT_SUCCESS; + } + + FILE *newHandle = NULL; + + if (strcmp(arg_in, "stdout") == 0) { + newHandle = stdout; + loggerConfig.file_name = "stdout"; + } else if (strcmp(arg_in, "stderr") == 0) { + newHandle = stderr; + loggerConfig.file_name = "stderr"; + } else { + newHandle = fopen(arg_in, "a"); + if (!newHandle) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + loggerConfig.file_name = arg_in; + } + + loggerConfig.output = newHandle; + LOG_INFO("Logger output changed from %s to %s", oldName, + loggerConfig.file_name); + + if (oldHandle && oldHandle != stdout && oldHandle != stderr) { + fclose(oldHandle); + } + + return UMF_RESULT_SUCCESS; } + +static const struct ctl_argument CTL_ARG(timestamp) = CTL_ARG_BOOLEAN; +static const struct ctl_argument CTL_ARG(pid) = CTL_ARG_BOOLEAN; +static const struct ctl_argument CTL_ARG(level) = CTL_ARG_INT; +static const struct ctl_argument CTL_ARG(flush_level) = CTL_ARG_INT; +static const struct ctl_argument + CTL_ARG(output) = CTL_ARG_STRING(MAX_FILE_PATH); + +const umf_ctl_node_t CTL_NODE(logger)[] = { + CTL_LEAF_RW(timestamp), CTL_LEAF_RW(pid), CTL_LEAF_RW(level), + CTL_LEAF_RW(flush_level), CTL_LEAF_RW(output), CTL_NODE_END, +}; +#endif diff --git a/src/utils/utils_log.h b/src/utils/utils_log.h index ab40121ce..c0e0a9572 100644 --- a/src/utils/utils_log.h +++ b/src/utils/utils_log.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -14,6 +14,8 @@ extern "C" { #endif +#include "ctl/ctl_internal.h" + typedef enum { LOG_DEBUG, LOG_INFO, @@ -47,6 +49,8 @@ void utils_plog(utils_log_level_t level, const char *func, const char *format, ...) __attribute__((format(printf, 3, 4))); #endif +extern const umf_ctl_node_t CTL_NODE(logger)[]; + #ifdef __cplusplus } #endif diff --git a/src/utils/utils_name.h b/src/utils/utils_name.h new file mode 100644 index 000000000..6914d06ba --- /dev/null +++ b/src/utils/utils_name.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#ifndef UMF_UTILS_NAME_H +#define UMF_UTILS_NAME_H + +#include +#include + +#include "utils_log.h" + +#define MAX_NAME 64 + +static inline int utils_name_is_valid(const char *name) { + if (!name) { + return 0; + } + size_t len = strlen(name); + if (len > MAX_NAME) { + return 0; + } + for (size_t i = 0; i < len; ++i) { + char c = name[i]; + if (!isalnum((unsigned char)c) && c != '-' && c != '_') { + return 0; + } + } + return 1; +} + +static inline void utils_warn_invalid_name(const char *kind, const char *name) { + if (!utils_name_is_valid(name)) { + LOG_WARN("%s name \"%s\" is deprecated. It should be no more than 64 " + "characters including null character, containing only " + "alphanumerics, '_' or '-'. CTL functionality may be limited.", + kind, name); + } +} + +#endif /* UMF_UTILS_NAME_H */ diff --git a/src/utils/utils_posix_concurrency.c b/src/utils/utils_posix_concurrency.c index 44a317361..c6f273bed 100644 --- a/src/utils/utils_posix_concurrency.c +++ b/src/utils/utils_posix_concurrency.c @@ -38,6 +38,11 @@ int utils_mutex_unlock(utils_mutex_t *m) { } void utils_init_once(UTIL_ONCE_FLAG *flag, void (*oneCb)(void)) { + if (oneCb == NULL) { + LOG_FATAL("utils_init_once: callback is NULL"); + return; + } + pthread_once(flag, oneCb); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d4e027e47..8862b7b88 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,20 +11,27 @@ if(CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM") add_link_options(-static-intel) endif() -include(FetchContent) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.15.2) - -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt - ON - CACHE BOOL "" FORCE) -set(INSTALL_GTEST - OFF - CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) +set(GTEST_VER 1.15.2) + +find_package(GTest ${GTEST_VER} QUIET) + +if(NOT GTest_FOUND) + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v${GTEST_VER}) + + # For Windows: Prevent overriding the parent project's compiler/linker + # settings + set(gtest_force_shared_crt + ON + CACHE BOOL "" FORCE) + set(INSTALL_GTEST + OFF + CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) +endif() enable_testing() set(UMF_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -50,6 +57,7 @@ function(build_umf_test) set(TEST_TARGET_NAME test_${ARG_NAME}) set(LIB_DIRS ${LIB_DIRS} ${LIBHWLOC_LIBRARY_DIRS}) + set(INC_DIRS ${INC_DIRS} ${LIBHWLOC_INCLUDE_DIRS}) if(UMF_CUDA_ENABLED) set(INC_DIRS ${INC_DIRS} ${CUDA_INCLUDE_DIRS}) @@ -61,10 +69,6 @@ function(build_umf_test) set(LIB_DIRS ${LIB_DIRS} ${ZE_LOADER_LIBRARY_DIRS}) endif() - if(NOT UMF_DISABLE_HWLOC) - set(INC_DIRS ${INC_DIRS} ${LIBHWLOC_INCLUDE_DIRS}) - endif() - if(UMF_POOL_JEMALLOC_ENABLED) set(CPL_DEFS ${CPL_DEFS} UMF_POOL_JEMALLOC_ENABLED=1) endif() @@ -195,10 +199,12 @@ add_umf_test( SRCS memoryProviderAPI.cpp LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) -add_umf_test( - NAME logger - SRCS utils/utils_log.cpp ${UMF_UTILS_SOURCES} - LIBS ${UMF_LOGGER_LIBS}) +if(NOT UMF_BUILD_SHARED_LIBRARY) + add_umf_test( + NAME logger + SRCS utils/utils_log.cpp ${UMF_UTILS_SOURCES} ../src/ctl/ctl.c + LIBS ${UMF_LOGGER_LIBS} ${UMF_BA_FOR_TEST}) +endif() add_umf_test( NAME ctl_unittest @@ -222,6 +228,26 @@ if(LINUX) LIBS ${UMF_UTILS_FOR_TEST}) endif() +build_umf_test( + NAME ctl_env_app + SRCS ctl/ctl_env_app.cpp + LIBS ${UMF_UTILS_FOR_TEST} umf) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ctl/ctl_env_config1.cfg + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ctl) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ctl/ctl_env_config2.cfg + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ctl) + +add_umf_test( + NAME ctl_env_driver + SRCS ctl/ctl_env_driver.cpp + LIBS ${UMF_UTILS_FOR_TEST}) + +target_compile_definitions( + test_ctl_env_driver + PRIVATE CTL_ENV_APP="$" + CTL_CONF_FILE_DIR="${CMAKE_CURRENT_BINARY_DIR}/ctl") + add_umf_test( NAME coarse_lib SRCS coarse_lib.cpp @@ -248,7 +274,7 @@ add_umf_test( SRCS c_api/disjoint_pool.c LIBS ${UMF_UTILS_FOR_TEST}) -if(LINUX AND (NOT UMF_DISABLE_HWLOC)) +if(LINUX) # this test uses the file provider add_umf_test( NAME disjoint_pool_file_prov @@ -256,28 +282,26 @@ if(LINUX AND (NOT UMF_DISABLE_HWLOC)) LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) endif() -if(UMF_POOL_JEMALLOC_ENABLED - AND UMF_POOL_SCALABLE_ENABLED - AND (NOT UMF_DISABLE_HWLOC)) +if(UMF_POOL_JEMALLOC_ENABLED AND UMF_POOL_SCALABLE_ENABLED) add_umf_test(NAME c_api_multi_pool SRCS c_api/multi_pool.c) endif() -if(UMF_POOL_JEMALLOC_ENABLED AND (NOT UMF_DISABLE_HWLOC)) +if(UMF_POOL_JEMALLOC_ENABLED) add_umf_test( NAME jemalloc_pool SRCS pools/jemalloc_pool.cpp malloc_compliance_tests.cpp LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) endif() -if(UMF_POOL_SCALABLE_ENABLED AND (NOT UMF_DISABLE_HWLOC)) +if(UMF_POOL_SCALABLE_ENABLED) add_umf_test( NAME scalable_pool SRCS pools/scalable_pool.cpp malloc_compliance_tests.cpp LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) endif() -if(LINUX AND (NOT UMF_DISABLE_HWLOC)) # OS-specific functions are implemented - # only for Linux now +if(LINUX) # OS-specific functions are implemented + # only for Linux now if(PkgConfig_FOUND) pkg_check_modules(LIBNUMA numa) endif() @@ -363,6 +387,10 @@ if(LINUX AND (NOT UMF_DISABLE_HWLOC)) # OS-specific functions are implemented NAME provider_tracking_fixture_tests SRCS provider_tracking_fixture_tests.cpp malloc_compliance_tests.cpp LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) + add_umf_test( + NAME provider_properties + SRCS properties/provider_properties.cpp + LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) # This test requires Linux-only file memory provider if(UMF_POOL_JEMALLOC_ENABLED) @@ -402,13 +430,6 @@ else() LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) endif() -if(UMF_DISABLE_HWLOC) - add_umf_test( - NAME provider_os_memory_not_impl - SRCS provider_os_memory_not_impl.cpp - LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) -endif() - if(UMF_BUILD_GPU_TESTS AND UMF_LEVEL_ZERO_ENABLED) # we have two test binaries here that use the same sources, but differ in # the way they are linked to the Level Zero (statically or at runtime using @@ -434,6 +455,12 @@ if(UMF_BUILD_GPU_TESTS AND UMF_LEVEL_ZERO_ENABLED) LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) target_compile_definitions(test_provider_level_zero_dlopen_local PUBLIC USE_DLOPEN=1 OPEN_ZE_LIBRARY_GLOBAL=0) + + add_umf_test( + NAME provider_properties_level_zero + SRCS properties/provider_properties_level_zero.cpp + ${UMF_UTILS_DIR}/utils_level_zero.cpp + LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST} ze_loader) endif() if(NOT UMF_BUILD_LEVEL_ZERO_PROVIDER) @@ -466,6 +493,12 @@ if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_CUDA_PROVIDER) LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST}) target_compile_definitions(test_provider_cuda_dlopen_local PUBLIC USE_DLOPEN=1 OPEN_CU_LIBRARY_GLOBAL=0) + + add_umf_test( + NAME provider_properties_cuda + SRCS properties/provider_properties_cuda.cpp + providers/cuda_helpers.cpp + LIBS ${UMF_UTILS_FOR_TEST} ${UMF_BA_FOR_TEST} cuda) else() message( STATUS @@ -570,7 +603,7 @@ function(add_umf_ipc_test) endfunction() if(LINUX) - if(NOT UMF_DISABLE_HWLOC AND UMF_POOL_SCALABLE_ENABLED) + if(UMF_POOL_SCALABLE_ENABLED) build_umf_test( NAME ipc_os_prov_consumer SRCS ipc_os_prov_consumer.c common/ipc_common.c @@ -649,8 +682,7 @@ endif() if(LINUX AND UMF_BUILD_SHARED_LIBRARY - AND UMF_POOL_SCALABLE_ENABLED - AND NOT UMF_DISABLE_HWLOC) + AND UMF_POOL_SCALABLE_ENABLED) add_umf_test( NAME init_teardown SRCS test_init_teardown.c @@ -740,7 +772,7 @@ if(LINUX ) endif() - if(EXAMPLES AND NOT UMF_DISABLE_HWLOC) + if(EXAMPLES) set(STANDALONE_CMAKE_OPTIONS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" ) diff --git a/test/coarse_lib.cpp b/test/coarse_lib.cpp index 41cac1128..598df11ca 100644 --- a/test/coarse_lib.cpp +++ b/test/coarse_lib.cpp @@ -114,7 +114,14 @@ INSTANTIATE_TEST_SUITE_P( CoarseWithMemoryStrategyTest, CoarseWithMemoryStrategyTest, ::testing::Values(UMF_COARSE_MEMORY_STRATEGY_FASTEST, UMF_COARSE_MEMORY_STRATEGY_FASTEST_BUT_ONE, - UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE)); + UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_COARSE_MEMORY_STRATEGY_FASTEST", + "UMF_COARSE_MEMORY_STRATEGY_FASTEST_BUT_ONE", + "UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE"}; + return names[info.index]; + })); TEST_P(CoarseWithMemoryStrategyTest, coarseTest_basic_provider) { umf_memory_provider_handle_t malloc_memory_provider; diff --git a/test/common/pool.hpp b/test/common/pool.hpp index 5cae85411..711472ebc 100644 --- a/test/common/pool.hpp +++ b/test/common/pool.hpp @@ -25,6 +25,38 @@ #include "provider.hpp" #include "utils/cpp_helpers.hpp" +typedef void *(*pfnPoolParamsCreate)(); +typedef umf_result_t (*pfnPoolParamsDestroy)(void *); + +typedef void *(*pfnProviderParamsCreate)(); +typedef umf_result_t (*pfnProviderParamsDestroy)(void *); + +using poolCreateExtParams = + std::tuple; + +std::string poolCreateExtParamsNameGen( + const testing::TestParamInfo &info) { + + const umf_memory_pool_ops_t *pool_ops = std::get<0>(info.param); + const umf_memory_provider_ops_t *provider_ops = std::get<3>(info.param); + + const char *poolName = NULL; + pool_ops->get_name(NULL, &poolName); + + const char *providerName = NULL; + provider_ops->get_name(NULL, &providerName); + + // if there are multiple cases with the same pool and provider combination, + // add the index to the name + std::string poolParams = std::get<1>(info.param) + ? "_w_params_" + std::to_string(info.index) + : ""; + + return std::string(poolName) + poolParams + "_" + providerName; +} + namespace umf_test { umf_memory_pool_handle_t @@ -108,25 +140,26 @@ typedef struct pool_base_t { umf_result_t initialize(umf_memory_provider_handle_t) noexcept { return UMF_RESULT_SUCCESS; }; - void *malloc([[maybe_unused]] size_t size) noexcept { return nullptr; } + void *malloc(size_t) noexcept { return nullptr; } void *calloc(size_t, size_t) noexcept { return nullptr; } void *realloc(void *, size_t) noexcept { return nullptr; } void *aligned_malloc(size_t, size_t) noexcept { return nullptr; } - umf_result_t malloc_usable_size(const void *, size_t *size) noexcept { - if (size) { - *size = 0; - } - return UMF_RESULT_SUCCESS; + umf_result_t malloc_usable_size(const void *, size_t *) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; } - umf_result_t free(void *) noexcept { return UMF_RESULT_SUCCESS; } + umf_result_t free(void *) noexcept { return UMF_RESULT_ERROR_UNKNOWN; } umf_result_t get_last_allocation_error() noexcept { - return UMF_RESULT_SUCCESS; + return UMF_RESULT_ERROR_UNKNOWN; } - umf_result_t get_name(const char **name) noexcept { - if (name) { - *name = "pool_base"; - } - return UMF_RESULT_SUCCESS; + umf_result_t get_name(const char **) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; + } + umf_result_t ext_ctl(umf_ctl_query_source_t, const char *, void *, size_t, + umf_ctl_query_type_t, va_list) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; + } + umf_result_t ext_trim_memory(size_t) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; } } pool_base_t; @@ -177,6 +210,11 @@ struct malloc_pool : public pool_base_t { } return UMF_RESULT_SUCCESS; } + + umf_result_t ext_trim_memory(size_t) noexcept { + // malloc_pool frees all memory immediately, so we have nothing to trim + return UMF_RESULT_SUCCESS; + } }; umf_memory_pool_ops_t MALLOC_POOL_OPS = diff --git a/test/common/pool_trace.c b/test/common/pool_trace.c index ce944479f..c05a16d32 100644 --- a/test/common/pool_trace.c +++ b/test/common/pool_trace.c @@ -99,6 +99,14 @@ static umf_result_t traceGetName(void *pool, const char **name) { return UMF_RESULT_SUCCESS; } +static umf_result_t traceTrimMemory(void *pool, size_t minBytesToKeep) { + trace_pool_t *trace_pool = (trace_pool_t *)pool; + + trace_pool->params.trace_handler(trace_pool->params.trace_context, + "trim_memory"); + return umfPoolTrimMemory(trace_pool->params.hUpstreamPool, minBytesToKeep); +} + umf_memory_pool_ops_t UMF_TRACE_POOL_OPS = { .version = UMF_POOL_OPS_VERSION_CURRENT, .initialize = traceInitialize, @@ -111,4 +119,5 @@ umf_memory_pool_ops_t UMF_TRACE_POOL_OPS = { .free = traceFree, .get_last_allocation_error = traceGetLastStatus, .get_name = traceGetName, + .ext_trim_memory = traceTrimMemory, }; diff --git a/test/common/provider.hpp b/test/common/provider.hpp index e52dd614a..b46c92305 100644 --- a/test/common/provider.hpp +++ b/test/common/provider.hpp @@ -18,6 +18,36 @@ #include "test_helpers.h" #include "utils/cpp_helpers.hpp" +typedef void *(*pfnProviderParamsCreate)(); +typedef umf_result_t (*pfnProviderParamsDestroy)(void *); + +using providerCreateExtParams = + std::tuple; + +std::string providerCreateExtParamsNameGen( + const testing::TestParamInfo param) { + const umf_memory_provider_ops_t *provider_ops = std::get<0>(param.param); + + const char *providerName = NULL; + provider_ops->get_name(NULL, &providerName); + + return providerName; +} + +void providerCreateExt(providerCreateExtParams params, + umf_test::provider_unique_handle_t *handle) { + umf_memory_provider_handle_t hProvider = nullptr; + auto [provider_ops, provider_params] = params; + + auto ret = + umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(hProvider, nullptr); + + *handle = umf_test::provider_unique_handle_t(hProvider, + &umfMemoryProviderDestroy); +} + namespace umf_test { umf_memory_provider_handle_t @@ -100,6 +130,29 @@ typedef struct provider_base_t { [[maybe_unused]] size_t size) noexcept { return UMF_RESULT_ERROR_NOT_SUPPORTED; } + + umf_result_t ext_ctl([[maybe_unused]] umf_ctl_query_source_t source, + [[maybe_unused]] const char *name, + [[maybe_unused]] void *arg, + [[maybe_unused]] size_t size, + [[maybe_unused]] umf_ctl_query_type_t queryType, + [[maybe_unused]] va_list args) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; + } + + umf_result_t ext_get_allocation_properties( + [[maybe_unused]] const void *ptr, + [[maybe_unused]] umf_memory_property_id_t memory_property_id, + [[maybe_unused]] void *value) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; + } + + umf_result_t ext_get_allocation_properties_size( + [[maybe_unused]] umf_memory_property_id_t memory_property_id, + [[maybe_unused]] size_t *size) noexcept { + return UMF_RESULT_ERROR_UNKNOWN; + } + virtual ~provider_base_t() = default; } provider_base_t; diff --git a/test/common/provider_null.c b/test/common/provider_null.c index 380cba47d..2ce8c78dd 100644 --- a/test/common/provider_null.c +++ b/test/common/provider_null.c @@ -5,9 +5,11 @@ #include #include -#include "provider_null.h" #include +#include "provider_null.h" +#include "utils_common.h" + static umf_result_t nullInitialize(const void *params, void **pool) { (void)params; *pool = NULL; @@ -22,9 +24,18 @@ static umf_result_t nullFinalize(void *pool) { static umf_result_t nullAlloc(void *provider, size_t size, size_t alignment, void **ptr) { (void)provider; - (void)size; - (void)alignment; - *ptr = NULL; + + if (ptr == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (size == 0) { + *ptr = NULL; + return UMF_RESULT_SUCCESS; + } + + *ptr = (void *)ALIGN_UP_SAFE(0xDEADBEAF, alignment); // any not-NULL value + return UMF_RESULT_SUCCESS; } @@ -133,6 +144,24 @@ static umf_result_t nullCloseIpcHandle(void *provider, void *ptr, size_t size) { return UMF_RESULT_SUCCESS; } +static umf_result_t +nullGetAllocationProperties(void *provider, const void *ptr, + umf_memory_property_id_t propertyId, void *value) { + (void)provider; + (void)ptr; + (void)propertyId; + (void)value; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t nullGetAllocationPropertiesSize( + void *provider, umf_memory_property_id_t propertyId, size_t *size) { + (void)provider; + (void)propertyId; + (void)size; + return UMF_RESULT_SUCCESS; +} + umf_memory_provider_ops_t UMF_NULL_PROVIDER_OPS = { .version = UMF_PROVIDER_OPS_VERSION_CURRENT, .initialize = nullInitialize, @@ -152,4 +181,6 @@ umf_memory_provider_ops_t UMF_NULL_PROVIDER_OPS = { .ext_put_ipc_handle = nullPutIpcHandle, .ext_open_ipc_handle = nullOpenIpcHandle, .ext_close_ipc_handle = nullCloseIpcHandle, + .ext_get_allocation_properties = nullGetAllocationProperties, + .ext_get_allocation_properties_size = nullGetAllocationPropertiesSize, }; diff --git a/test/common/provider_trace.c b/test/common/provider_trace.c index adb808336..403870511 100644 --- a/test/common/provider_trace.c +++ b/test/common/provider_trace.c @@ -5,10 +5,12 @@ #include #include -#include "provider_trace.h" #include #include +#include "memory_properties_internal.h" +#include "provider_trace.h" + static umf_result_t traceInitialize(const void *params, void **pool) { umf_provider_trace_params_t *trace_pool = (umf_provider_trace_params_t *)malloc( @@ -214,4 +216,6 @@ umf_memory_provider_ops_t UMF_TRACE_PROVIDER_OPS = { .ext_put_ipc_handle = tracePutIpcHandle, .ext_open_ipc_handle = traceOpenIpcHandle, .ext_close_ipc_handle = traceCloseIpcHandle, + .ext_ctl = NULL, + .ext_get_allocation_properties = NULL, }; diff --git a/test/ctl/ctl_api.cpp b/test/ctl/ctl_api.cpp index 55120961b..0037d3dbb 100644 --- a/test/ctl/ctl_api.cpp +++ b/test/ctl/ctl_api.cpp @@ -378,3 +378,245 @@ TEST_F(CtlTest, ctlDefaultMultithreadedProvider) { } } #endif + +TEST_F(test, ctl_logger_basic_rw) { + bool ts_set = true; + ASSERT_EQ(umfCtlSet("umf.logger.timestamp", &ts_set, sizeof(ts_set)), + UMF_RESULT_SUCCESS); + bool ts_get = false; + ASSERT_EQ(umfCtlGet("umf.logger.timestamp", &ts_get, sizeof(ts_get)), + UMF_RESULT_SUCCESS); + EXPECT_TRUE(ts_get); + + bool pid_set = 1; + ASSERT_EQ(umfCtlSet("umf.logger.pid", &pid_set, sizeof(pid_set)), + UMF_RESULT_SUCCESS); + bool pid_get = 0; + ASSERT_EQ(umfCtlGet("umf.logger.pid", &pid_get, sizeof(pid_get)), + UMF_RESULT_SUCCESS); + EXPECT_EQ(pid_get, 1); + + int level_set = 1; + ASSERT_EQ(umfCtlSet("umf.logger.level", &level_set, sizeof(level_set)), + UMF_RESULT_SUCCESS); + int level_get = 0; + ASSERT_EQ(umfCtlGet("umf.logger.level", &level_get, sizeof(level_get)), + UMF_RESULT_SUCCESS); + EXPECT_EQ(level_get, 1); + + int flush_set = 2; + ASSERT_EQ( + umfCtlSet("umf.logger.flush_level", &flush_set, sizeof(flush_set)), + UMF_RESULT_SUCCESS); + int flush_get = 0; + ASSERT_EQ( + umfCtlGet("umf.logger.flush_level", &flush_get, sizeof(flush_get)), + UMF_RESULT_SUCCESS); + EXPECT_EQ(flush_get, 2); + + const char *out_name = "stdout"; + ASSERT_EQ(umfCtlSet("umf.logger.output", &out_name, sizeof(out_name)), + UMF_RESULT_SUCCESS); + const char *out_get = NULL; + ASSERT_EQ(umfCtlGet("umf.logger.output", &out_get, sizeof(out_get)), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(out_get, "stdout"); +} + +TEST_F(test, ctl_logger_output_file) { + const char *file_name = "ctl_log.txt"; + ASSERT_EQ(umfCtlSet("umf.logger.output", &file_name, sizeof(file_name)), + UMF_RESULT_SUCCESS); + const char *out_get = NULL; + ASSERT_EQ(umfCtlGet("umf.logger.output", &out_get, sizeof(out_get)), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(out_get, file_name); +} + +TEST_F(test, ctl_by_name) { + umf_memory_provider_handle_t hProvider = NULL; + umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL; + const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps(); + if (os_provider_ops == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params); + ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params, + &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + umfOsMemoryProviderParamsDestroy(os_memory_provider_params); + + umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL; + ret = umfDisjointPoolParamsCreate(&disjoint_pool_params); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name = "test_disjoint_pool"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name2 = "test_disjoint_pool2"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool2 = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool2); + umfDisjointPoolParamsDestroy(disjoint_pool_params); + + size_t pool_count; + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 1ull); + + size_t alloc_count; + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name2); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + // allocate from pool1 + void *ptr1 = umfPoolMalloc(hPool, 1024); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr1, nullptr); + + // we can use pool name in the string without {} too + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfPoolFree(hPool, ptr1); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + // we can use index parameter too + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.0.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.{}.stats.alloc_count", + &alloc_count, sizeof(alloc_count), 0); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + // test too big pool index + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool2.10.stats.alloc_count", + &alloc_count, sizeof(alloc_count)); + ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfPoolDestroy(hPool); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfMemoryProviderDestroy(hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); +} + +TEST_F(test, ctl_by_name_collision) { + umf_memory_provider_handle_t hProvider = NULL; + umf_os_memory_provider_params_handle_t os_memory_provider_params = NULL; + const umf_memory_provider_ops_t *os_provider_ops = umfOsMemoryProviderOps(); + if (os_provider_ops == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + int ret = umfOsMemoryProviderParamsCreate(&os_memory_provider_params); + ret = umfMemoryProviderCreate(os_provider_ops, os_memory_provider_params, + &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + umfOsMemoryProviderParamsDestroy(os_memory_provider_params); + + umf_disjoint_pool_params_handle_t disjoint_pool_params = NULL; + ret = umfDisjointPoolParamsCreate(&disjoint_pool_params); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *pool_name = "test_disjoint_pool"; + ret = umfDisjointPoolParamsSetName(disjoint_pool_params, pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_pool_handle_t hPool2 = NULL; + ret = umfPoolCreate(umfDisjointPoolOps(), hProvider, disjoint_pool_params, + 0, &hPool2); + umfDisjointPoolParamsDestroy(disjoint_pool_params); + + // allocate from pool1 + void *ptr1 = umfPoolMalloc(hPool, 1024); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr1, nullptr); + + size_t pool_count; + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.count", &pool_count, + sizeof(pool_count)); + + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(pool_count, 2ull); + + // If there is more than one pool with the same name, + // CtlGet by_name will return an error + size_t alloc_count; + ret = umfCtlGet("umf.pool.by_name.{}.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + + ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // ctl set and exec will still work. But there is no CTL entry for now to test it + + // todo: add test when ctl entries will be extended + + // we can read from specific pool with index argument + ret = umfCtlGet("umf.pool.by_name.test_disjoint_pool.0.stats.alloc_count", + &alloc_count, sizeof(alloc_count), pool_name, 0); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 1ull); + + ret = umfCtlGet("umf.pool.by_name.{}.1.stats.alloc_count", &alloc_count, + sizeof(alloc_count), pool_name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(alloc_count, 0ull); + + ret = umfPoolFree(hPool, ptr1); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfPoolDestroy(hPool2); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ret = umfMemoryProviderDestroy(hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); +} diff --git a/test/ctl/ctl_debug.c b/test/ctl/ctl_debug.c index e8730d896..ddff10eea 100644 --- a/test/ctl/ctl_debug.c +++ b/test/ctl/ctl_debug.c @@ -11,6 +11,7 @@ * ctl_debug.c -- implementation of the debug CTL namespace */ +#include #include #include "ctl/ctl_internal.h" @@ -183,4 +184,7 @@ static const umf_ctl_node_t CTL_NODE(debug)[] = { */ void debug_ctl_register(struct ctl *ctl) { CTL_REGISTER_MODULE(ctl, debug); } -void initialize_debug_ctl(void) { debug_ctl_register(&ctl_debug); } +void initialize_debug_ctl(void) { + debug_ctl_register(&ctl_debug); + ctl_init(malloc, free); +} diff --git a/test/ctl/ctl_env_app.cpp b/test/ctl/ctl_env_app.cpp new file mode 100644 index 000000000..45d5b7829 --- /dev/null +++ b/test/ctl/ctl_env_app.cpp @@ -0,0 +1,57 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#include +#include +#include +#include + +#include +#include + +static int test_env_defaults(int argc, char **argv) { + char buf[64] = {0}; + + if (argc % 2 != 0) { + std::cerr << "expected even number of arguments" << std::endl; + std::cerr << "Usage: env_defaults key1 value1 key2 value2 ..." + << std::endl; + return 1; + } + for (int i = 0; i < argc; i += 2) { + const char *key = argv[i]; + const char *value = argv[i + 1]; + if (umfCtlGet(key, buf, sizeof(buf)) != UMF_RESULT_SUCCESS) { + fprintf(stderr, "Failed to get control for '%s'\n", key); + return 1; + } + + if (strcmp(buf, value) != 0) { + std::cerr << "Expected value for '" << key << "' to be '" << value + << "', but got '" << buf << "'" << std::endl; + return 1; + } + } + return 0; +} + +int main(int argc, char **argv) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " args..." + << std::endl; + return 1; + } + const char *test_name = argv[1]; + argc -= 2; + argv += 2; + if (strcmp(test_name, "env_defaults") == 0) { + return test_env_defaults(argc, argv); + } + return 1; +} diff --git a/test/ctl/ctl_env_config1.cfg b/test/ctl/ctl_env_config1.cfg new file mode 100644 index 000000000..9831dc352 --- /dev/null +++ b/test/ctl/ctl_env_config1.cfg @@ -0,0 +1,4 @@ +# Copyright (C) 2025 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +umf.pool.default.test_pool.opt_one=opt_one_value1; # test comment diff --git a/test/ctl/ctl_env_config2.cfg b/test/ctl/ctl_env_config2.cfg new file mode 100644 index 000000000..ca2b52d62 --- /dev/null +++ b/test/ctl/ctl_env_config2.cfg @@ -0,0 +1,5 @@ +# Copyright (C) 2025 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +umf.pool.default.test_pool.opt_one=opt_one_value2; +umf.pool.default.test_pool.opt_two=opt_two_value2; diff --git a/test/ctl/ctl_env_driver.cpp b/test/ctl/ctl_env_driver.cpp new file mode 100644 index 000000000..bcc795354 --- /dev/null +++ b/test/ctl/ctl_env_driver.cpp @@ -0,0 +1,123 @@ +/* + * + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include +#include + +#include "../common/base.hpp" +#include "gtest/gtest.h" + +using namespace umf_test; + +#ifndef CTL_ENV_APP +#define CTL_ENV_APP "./ctl_env_app" +#endif + +#ifndef CTL_CONF_FILE_DIR +#define CTL_CONF_FILE_DIR "./ctl" +#endif + +void set_env(std::pair env) { + const auto &name = env.first; + const auto &value = env.second; + + if (name.empty()) { + return; + } +#ifdef _WIN32 + _putenv_s(name.c_str(), value.c_str()); +#else + setenv(name.c_str(), value.c_str(), 1); +#endif +} + +static void run_case( + const std::vector> &env, + const std::vector &args) { + for (const auto &e : env) { + set_env(e); + } + +#ifdef _WIN32 + std::vector cargs; + cargs.push_back(CTL_ENV_APP); + for (const auto &s : args) { + cargs.push_back(s.c_str()); + } + + cargs.push_back(nullptr); + intptr_t status = _spawnv(_P_WAIT, CTL_ENV_APP, cargs.data()); + ASSERT_EQ(status, 0); +#else + pid_t pid = fork(); + if (pid == 0) { + std::vector cargs; + cargs.push_back(const_cast(CTL_ENV_APP)); + for (const auto &s : args) { + cargs.push_back(const_cast(s.c_str())); + } + cargs.push_back(nullptr); + execv(CTL_ENV_APP, cargs.data()); + std::cerr << "Failed to execute " << CTL_ENV_APP << std::endl; + _exit(127); + } + int status = 0; + waitpid(pid, &status, 0); + ASSERT_EQ(status, 0); +#endif + for (const auto &e : env) { + set_env({e.first, ""}); // Clear the environment variable + } +} + +TEST_F(test, ctl_env_defaults) { + run_case( + {{"UMF_CONF", "umf.pool.default.test_pool.opt_one=test_value"}}, + {"env_defaults", "umf.pool.default.test_pool.opt_one", "test_value"}); + + run_case({{"UMF_CONF", "umf.pool.default.test_pool.opt_one=second"}}, + {"env_defaults", "umf.pool.default.test_pool.opt_one", "second"}); +} + +TEST_F(test, ctl_env_file) { + std::string cfg1 = CTL_CONF_FILE_DIR "/ctl_env_config1.cfg"; + std::string cfg2 = CTL_CONF_FILE_DIR "/ctl_env_config2.cfg"; + + run_case({{"UMF_CONF_FILE", cfg1}}, + {"env_defaults", "umf.pool.default.test_pool.opt_one", + "opt_one_value1"}); + + run_case({{"UMF_CONF_FILE", cfg2}}, + {"env_defaults", "umf.pool.default.test_pool.opt_one", + "opt_one_value2", "umf.pool.default.test_pool.opt_two", + "opt_two_value2"}); +} + +TEST_F(test, ctl_env_plus_file) { + std::string cfg = CTL_CONF_FILE_DIR "/ctl_env_config2.cfg"; + + // it is expected that configuration from file will override configuration from environment variable + run_case({{"UMF_CONF_FILE", cfg}, + {"UMF_CONF", "umf.pool.default.test_pool.opt_one=first;umf.pool." + "default.test_pool.opt_three=second"}}, + {"env_defaults", "umf.pool.default.test_pool.opt_one", + "opt_one_value2", "umf.pool.default.test_pool.opt_two", + "opt_two_value2", "umf.pool.default.test_pool.opt_three", + "second"}); +} diff --git a/test/disjoint_pool_file_prov.cpp b/test/disjoint_pool_file_prov.cpp index 607d265e0..817a0c108 100644 --- a/test/disjoint_pool_file_prov.cpp +++ b/test/disjoint_pool_file_prov.cpp @@ -37,7 +37,13 @@ INSTANTIATE_TEST_SUITE_P( FileWithMemoryStrategyTest, FileWithMemoryStrategyTest, ::testing::Values(UMF_COARSE_MEMORY_STRATEGY_FASTEST, UMF_COARSE_MEMORY_STRATEGY_FASTEST_BUT_ONE, - UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE)); + UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE), + ([](auto const &info) -> std::string { + const char *names[] = {"UMF_COARSE_MEMORY_STRATEGY_FASTEST", + "UMF_COARSE_MEMORY_STRATEGY_FASTEST_BUT_ONE", + "UMF_COARSE_MEMORY_STRATEGY_CHECK_ALL_SIZE"}; + return names[info.index]; + })); TEST_P(FileWithMemoryStrategyTest, disjointFileMallocPool_simple1) { umf_memory_provider_handle_t malloc_memory_provider = nullptr; diff --git a/test/ipcAPI.cpp b/test/ipcAPI.cpp index bd3f412da..c9496f863 100644 --- a/test/ipcAPI.cpp +++ b/test/ipcAPI.cpp @@ -128,4 +128,5 @@ INSTANTIATE_TEST_SUITE_P(umfIpcTestSuite, umfIpcTest, ::testing::Values(ipcTestParams{ umfProxyPoolOps(), nullptr, nullptr, &IPC_MOCK_PROVIDER_OPS, nullptr, nullptr, - &hostMemoryAccessor})); + &hostMemoryAccessor}), + ipcTestParamsNameGen); diff --git a/test/ipcFixtures.hpp b/test/ipcFixtures.hpp index 4c1e5e714..1bcd18a29 100644 --- a/test/ipcFixtures.hpp +++ b/test/ipcFixtures.hpp @@ -5,21 +5,21 @@ #ifndef UMF_TEST_IPC_FIXTURES_HPP #define UMF_TEST_IPC_FIXTURES_HPP -#include "base.hpp" -#include "multithread_helpers.hpp" -#include "pool.hpp" -#include "test_helpers.h" +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include +#include "base.hpp" +#include "multithread_helpers.hpp" +#include "pool.hpp" +#include "test_helpers.h" class MemoryAccessor { public: @@ -27,6 +27,7 @@ class MemoryAccessor { virtual void fill(void *ptr, size_t size, const void *pattern, size_t pattern_size) = 0; virtual void copy(void *dst_ptr, void *src_ptr, size_t size) = 0; + virtual const char *getName() = 0; }; class HostMemoryAccessor : public MemoryAccessor { @@ -47,6 +48,8 @@ class HostMemoryAccessor : public MemoryAccessor { void copy(void *dst_ptr, void *src_ptr, size_t size) override { std::memcpy(dst_ptr, src_ptr, size); } + + const char *getName() override { return "HostMemoryAccessor"; } }; typedef void *(*pfnPoolParamsCreate)(); @@ -65,6 +68,29 @@ using ipcTestParams = pfnProviderParamsCreate, pfnProviderParamsDestroy, MemoryAccessor *>; +std::string +ipcTestParamsNameGen(const ::testing::TestParamInfo &info) { + const umf_memory_pool_ops_t *pool_ops = std::get<0>(info.param); + const umf_memory_provider_ops_t *provider_ops = std::get<3>(info.param); + + const char *poolName = NULL; + pool_ops->get_name(NULL, &poolName); + + const char *providerName = NULL; + provider_ops->get_name(NULL, &providerName); + + // if there are multiple cases with the same pool and provider combination, + // add index to the name + std::string poolParams = std::get<1>(info.param) + ? "_w_params_" + std::to_string(info.index) + : ""; + + MemoryAccessor *memAccessor = std::get<6>(info.param); + + return std::string(poolName) + poolParams + "_" + providerName + "_" + + memAccessor->getName(); +} + struct umfIpcTest : umf_test::test, ::testing::WithParamInterface { umfIpcTest() {} diff --git a/test/ipc_devdax_prov_consumer.c b/test/ipc_devdax_prov_consumer.c index 760d075c8..105ddd864 100644 --- a/test/ipc_devdax_prov_consumer.c +++ b/test/ipc_devdax_prov_consumer.c @@ -23,13 +23,13 @@ int main(int argc, char *argv[]) { int port = atoi(argv[1]); char *path = getenv("UMF_TESTS_DEVDAX_PATH"); - if (path == NULL || path[0] == 0) { + if (path == NULL || path[0] == '\0') { fprintf(stderr, "Test skipped, UMF_TESTS_DEVDAX_PATH is not set\n"); return 0; } char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (size == NULL || size[0] == 0) { + if (size == NULL || size[0] == '\0') { fprintf(stderr, "Test skipped, UMF_TESTS_DEVDAX_SIZE is not set\n"); return 0; } diff --git a/test/ipc_devdax_prov_producer.c b/test/ipc_devdax_prov_producer.c index 39d598599..2445db07e 100644 --- a/test/ipc_devdax_prov_producer.c +++ b/test/ipc_devdax_prov_producer.c @@ -23,13 +23,13 @@ int main(int argc, char *argv[]) { int port = atoi(argv[1]); char *path = getenv("UMF_TESTS_DEVDAX_PATH"); - if (path == NULL || path[0] == 0) { + if (path == NULL || path[0] == '\0') { fprintf(stderr, "Test skipped, UMF_TESTS_DEVDAX_PATH is not set\n"); return 0; } char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (size == NULL || size[0] == 0) { + if (size == NULL || size[0] == '\0') { fprintf(stderr, "Test skipped, UMF_TESTS_DEVDAX_SIZE is not set\n"); return 0; } diff --git a/test/memoryPoolAPI.cpp b/test/memoryPoolAPI.cpp index 16d7afd58..f2cfb61bb 100644 --- a/test/memoryPoolAPI.cpp +++ b/test/memoryPoolAPI.cpp @@ -3,28 +3,35 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This file contains tests for UMF pool API -#include "base.hpp" -#include "pool.hpp" -#include "poolFixtures.hpp" -#include "provider.hpp" -#include "provider_null.h" -#include "provider_trace.h" -#include "test_helpers.h" +#include +#include +#include +#include +#include +#include #include #include +#include #include +#include + +#ifdef UMF_POOL_JEMALLOC_ENABLED +#include +#include +#endif #ifdef UMF_PROXY_LIB_ENABLED #include #endif -#include -#include -#include -#include -#include -#include +#include "base.hpp" +#include "pool.hpp" +#include "poolFixtures.hpp" +#include "provider.hpp" +#include "provider_null.h" +#include "provider_trace.h" +#include "test_helpers.h" using umf_test::test; using namespace umf_test; @@ -79,7 +86,8 @@ TEST_P(umfPoolWithCreateFlagsTest, memoryPoolTrace) { size_t tmpSize; umfPoolMallocUsableSize(tracingPool.get(), nullptr, &tmpSize); - // we ignore return value of poolMallocUsabeSize(), as it might be not supported + // we ignore return value of umfPoolMallocUsableSize(), as it might be not + // supported ASSERT_EQ(poolCalls["malloc_usable_size"], 1UL); ASSERT_EQ(poolCalls.size(), ++pool_call_count); @@ -112,6 +120,12 @@ TEST_P(umfPoolWithCreateFlagsTest, memoryPoolTrace) { ASSERT_EQ(poolCalls["get_last_native_error"], 1UL); ASSERT_EQ(poolCalls.size(), ++pool_call_count); + umfPoolTrimMemory(tracingPool.get(), 0); + // we ignore return value of umfPoolTrimMemory(), as it might be not + // supported + ASSERT_EQ(poolCalls["trim_memory"], 1UL); + ASSERT_EQ(poolCalls.size(), ++pool_call_count); + if (manuallyDestroyProvider) { umfMemoryProviderDestroy(provider); } @@ -126,6 +140,13 @@ TEST_P(umfPoolWithCreateFlagsTest, memoryPoolWithCustomProvider) { EXPECT_NE_NOEXCEPT(provider, nullptr); return UMF_RESULT_SUCCESS; } + + umf_result_t get_name(const char **name) noexcept { + if (name) { + *name = "pool"; + } + return UMF_RESULT_SUCCESS; + } }; umf_memory_pool_ops_t pool_ops = umf_test::poolMakeCOps(); @@ -300,6 +321,31 @@ TEST_F(tagTest, SetAndGetInvalidPool) { ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); } +#ifdef UMF_POOL_JEMALLOC_ENABLED +static void *createOsMemoryProviderParams() { + umf_os_memory_provider_params_handle_t params = nullptr; + umf_result_t res = umfOsMemoryProviderParamsCreate(¶ms); + if (res != UMF_RESULT_SUCCESS) { + throw std::runtime_error("Failed to create os memory provider params"); + } + + return params; +} + +static umf_result_t destroyOsMemoryProviderParams(void *params) { + return umfOsMemoryProviderParamsDestroy( + (umf_os_memory_provider_params_handle_t)params); +} + +INSTANTIATE_TEST_SUITE_P( + jemallocPoolTest, umfPoolTest, + ::testing::Values(poolCreateExtParams{ + umfJemallocPoolOps(), nullptr, nullptr, umfOsMemoryProviderOps(), + createOsMemoryProviderParams, destroyOsMemoryProviderParams}), + poolCreateExtParamsNameGen); + +#endif /* UMF_POOL_JEMALLOC_ENABLED */ + INSTANTIATE_TEST_SUITE_P( mallocPoolTest, umfPoolTest, ::testing::Values( @@ -309,16 +355,31 @@ INSTANTIATE_TEST_SUITE_P( &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}, poolCreateExtParams{umfDisjointPoolOps(), defaultDisjointPoolConfig, defaultDisjointPoolConfigDestroy, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); + +#ifdef UMF_POOL_SCALABLE_ENABLED +INSTANTIATE_TEST_SUITE_P(mallocPoolTestScalable, umfPoolTest, + ::testing::Values(poolCreateExtParams{ + umfScalablePoolOps(), nullptr, nullptr, + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); +#endif INSTANTIATE_TEST_SUITE_P(mallocMultiPoolTest, umfMultiPoolTest, ::testing::Values(poolCreateExtParams{ umfProxyPoolOps(), nullptr, nullptr, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); INSTANTIATE_TEST_SUITE_P(umfPoolWithCreateFlagsTest, umfPoolWithCreateFlagsTest, ::testing::Values(0, - UMF_POOL_CREATE_FLAG_OWN_PROVIDER)); + UMF_POOL_CREATE_FLAG_OWN_PROVIDER), + ([](auto const &info) -> std::string { + static const char *names[] = { + "NONE", "UMF_POOL_CREATE_FLAG_OWN_PROVIDER"}; + return names[info.index]; + })); ////////////////// Negative test cases ///////////////// @@ -382,7 +443,14 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY, UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC, UMF_RESULT_ERROR_INVALID_ARGUMENT, - UMF_RESULT_ERROR_UNKNOWN)); + UMF_RESULT_ERROR_UNKNOWN), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY", + "UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC", + "UMF_RESULT_ERROR_INVALID_ARGUMENT", "UMF_RESULT_ERROR_UNKNOWN"}; + return names[info.index]; + })); TEST_P(poolInitializeTest, errorPropagation) { auto nullProvider = umf_test::wrapProviderUnique(nullProviderCreate()); @@ -559,4 +627,19 @@ INSTANTIATE_TEST_SUITE_P( umf_test::withGeneratedArgs(umfPoolGetMemoryProvider), umf_test::withGeneratedArgs(umfPoolByPtr), umf_test::withGeneratedArgs(umfPoolSetTag), - umf_test::withGeneratedArgs(umfPoolGetTag))); + umf_test::withGeneratedArgs(umfPoolGetTag)), + ([](auto const &info) -> std::string { + static const char *names[] = {"umfPoolMalloc", + "umfPoolAlignedMalloc", + "umfPoolFree", + "umfPoolCalloc", + "umfPoolRealloc", + "umfPoolMallocUsableSize", + "umfPoolGetLastAllocationError", + "umfPoolGetName", + "umfPoolGetMemoryProvider", + "umfPoolByPtr", + "umfPoolSetTag", + "umfPoolGetTag"}; + return names[info.index]; + })); diff --git a/test/memoryProviderAPI.cpp b/test/memoryProviderAPI.cpp index 33e298dc6..5cbc8c2ca 100644 --- a/test/memoryProviderAPI.cpp +++ b/test/memoryProviderAPI.cpp @@ -3,15 +3,16 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This file contains tests for UMF provider API -#include "provider.hpp" -#include "provider_null.h" -#include "test_helpers.h" - #include #include #include #include +#include "memory_properties_internal.h" +#include "provider.hpp" +#include "provider_null.h" +#include "test_helpers.h" + using umf_test::test; TEST_F(test, memoryProviderTrace) { @@ -26,7 +27,7 @@ TEST_F(test, memoryProviderTrace) { auto tracingProvider = umf_test::wrapProviderUnique( traceProviderCreate(nullProvider, true, &calls, trace)); - size_t call_count = 0; + size_t call_count = 1; // get_name is called during initialization void *ptr; auto ret = umfMemoryProviderAlloc(tracingProvider.get(), 0, 0, &ptr); @@ -64,8 +65,8 @@ TEST_F(test, memoryProviderTrace) { ret = umfMemoryProviderGetName(tracingProvider.get(), &pName); ASSERT_EQ(ret, UMF_RESULT_SUCCESS); ASSERT_NE(pName, nullptr); - ASSERT_EQ(calls["name"], 1UL); - ASSERT_EQ(calls.size(), ++call_count); + ASSERT_EQ(calls["name"], 2UL); + ASSERT_EQ(calls.size(), call_count); ASSERT_EQ(std::string(pName), std::string("null")); ret = umfMemoryProviderPurgeLazy(tracingProvider.get(), &page_size, @@ -338,7 +339,14 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY, UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC, UMF_RESULT_ERROR_INVALID_ARGUMENT, - UMF_RESULT_ERROR_UNKNOWN)); + UMF_RESULT_ERROR_UNKNOWN), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY", + "UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC", + "UMF_RESULT_ERROR_INVALID_ARGUMENT", "UMF_RESULT_ERROR_UNKNOWN"}; + return names[info.index]; + })); TEST_P(providerInitializeTest, errorPropagation) { struct provider : public umf_test::provider_base_t { @@ -389,4 +397,14 @@ INSTANTIATE_TEST_SUITE_P( umf_test::withGeneratedArgs(umfMemoryProviderGetMinPageSize), umf_test::withGeneratedArgs(umfMemoryProviderPurgeLazy), umf_test::withGeneratedArgs(umfMemoryProviderPurgeForce), - umf_test::withGeneratedArgs(umfMemoryProviderGetName))); + umf_test::withGeneratedArgs(umfMemoryProviderGetName)), + ([](auto const &info) -> std::string { + static const char *names[] = {"umfMemoryProviderAlloc", + "umfMemoryProviderFree", + "umfMemoryProviderGetRecommendedPageSize", + "umfMemoryProviderGetMinPageSize", + "umfMemoryProviderPurgeLazy", + "umfMemoryProviderPurgeForce", + "umfMemoryProviderGetName"}; + return names[info.index]; + })); diff --git a/test/memspaces/memspace_highest_bandwidth.cpp b/test/memspaces/memspace_highest_bandwidth.cpp index 5bedac0ea..5ad19baed 100644 --- a/test/memspaces/memspace_highest_bandwidth.cpp +++ b/test/memspaces/memspace_highest_bandwidth.cpp @@ -40,16 +40,25 @@ static void canQueryBandwidth(size_t nodeId) { } } -INSTANTIATE_TEST_SUITE_P(memspaceLowestLatencyTest, memspaceGetTest, - ::testing::Values(memspaceGetParams{ - canQueryBandwidth, - umfMemspaceHighestBandwidthGet})); - -INSTANTIATE_TEST_SUITE_P(memspaceLowestLatencyProviderTest, - memspaceProviderTest, - ::testing::Values(memspaceGetParams{ - canQueryBandwidth, - umfMemspaceHighestBandwidthGet})); +INSTANTIATE_TEST_SUITE_P( + memspaceLowestLatencyTest, memspaceGetTest, + ::testing::Values(memspaceGetParams{canQueryBandwidth, + umfMemspaceHighestBandwidthGet}), + ([](auto const &info) -> std::string { + static const char *names[] = {"canQueryBandwidth", + "umfMemspaceHighestBandwidthGet"}; + return names[info.index]; + })); + +INSTANTIATE_TEST_SUITE_P( + memspaceLowestLatencyProviderTest, memspaceProviderTest, + ::testing::Values(memspaceGetParams{canQueryBandwidth, + umfMemspaceHighestBandwidthGet}), + ([](auto const &info) -> std::string { + static const char *names[] = {"canQueryBandwidth", + "umfMemspaceHighestBandwidthGet"}; + return names[info.index]; + })); TEST_F(numaNodesTest, PerCoreBandwidthPlacement) { const size_t allocSize = 4096; diff --git a/test/memspaces/memspace_lowest_latency.cpp b/test/memspaces/memspace_lowest_latency.cpp index 02fdd481e..78e8412de 100644 --- a/test/memspaces/memspace_lowest_latency.cpp +++ b/test/memspaces/memspace_lowest_latency.cpp @@ -41,9 +41,21 @@ static void canQueryLatency(size_t nodeId) { INSTANTIATE_TEST_SUITE_P(memspaceLowestLatencyTest, memspaceGetTest, ::testing::Values(memspaceGetParams{ - canQueryLatency, umfMemspaceLowestLatencyGet})); + canQueryLatency, umfMemspaceLowestLatencyGet}), + ([](auto const &info) -> std::string { + static const char *names[] = { + "canQueryLatency", + "umfMemspaceLowestLatencyGet"}; + return names[info.index]; + })); INSTANTIATE_TEST_SUITE_P(memspaceLowestLatencyProviderTest, memspaceProviderTest, ::testing::Values(memspaceGetParams{ - canQueryLatency, umfMemspaceLowestLatencyGet})); + canQueryLatency, umfMemspaceLowestLatencyGet}), + ([](auto const &info) -> std::string { + static const char *names[] = { + "canQueryLatency", + "umfMemspaceLowestLatencyGet"}; + return names[info.index]; + })); diff --git a/test/poolFixtures.hpp b/test/poolFixtures.hpp index 98778cd56..2b4275023 100644 --- a/test/poolFixtures.hpp +++ b/test/poolFixtures.hpp @@ -23,17 +23,6 @@ #include "provider.hpp" #include "utils/utils_sanitizers.h" -typedef void *(*pfnPoolParamsCreate)(); -typedef umf_result_t (*pfnPoolParamsDestroy)(void *); - -typedef void *(*pfnProviderParamsCreate)(); -typedef umf_result_t (*pfnProviderParamsDestroy)(void *); - -using poolCreateExtParams = - std::tuple; - umf_test::pool_unique_handle_t poolCreateExtUnique(poolCreateExtParams params) { auto [pool_ops, poolParamsCreate, poolParamsDestroy, provider_ops, providerParamsCreate, providerParamsDestroy] = params; @@ -51,6 +40,10 @@ umf_test::pool_unique_handle_t poolCreateExtUnique(poolCreateExtParams params) { &upstream_provider); EXPECT_EQ(ret, UMF_RESULT_SUCCESS); EXPECT_NE(upstream_provider, nullptr); + if (ret != UMF_RESULT_SUCCESS || upstream_provider == nullptr) { + assert(false && "Failed to create a memory provider"); + return umf_test::pool_unique_handle_t(nullptr, nullptr); + } provider = upstream_provider; @@ -65,6 +58,10 @@ umf_test::pool_unique_handle_t poolCreateExtUnique(poolCreateExtParams params) { UMF_POOL_CREATE_FLAG_OWN_PROVIDER, &hPool); EXPECT_EQ(ret, UMF_RESULT_SUCCESS); EXPECT_NE(hPool, nullptr); + if (ret != UMF_RESULT_SUCCESS || hPool == nullptr) { + assert(false && "Failed to create a memory pool"); + return umf_test::pool_unique_handle_t(nullptr, nullptr); + } // we do not need params anymore if (poolParamsDestroy) { @@ -133,6 +130,11 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfMemTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfPoolTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfMultiPoolTest); +TEST_P(umfPoolTest, destroyNullptr) { + auto ret = umfPoolDestroy(nullptr); + ASSERT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); +} + TEST_P(umfPoolTest, allocFree) { static constexpr size_t allocSize = 64; auto *ptr = umfPoolMalloc(pool.get(), allocSize); @@ -403,6 +405,32 @@ TEST_P(umfPoolTest, multiThreadedMallocFreeRandomSizes) { } } +TEST_P(umfPoolTest, trimMemory) { + constexpr size_t size = 1024; + + umf_memory_pool_handle_t hPool = pool.get(); + void *ptr = umfPoolMalloc(hPool, size); + ASSERT_NE(ptr, nullptr); + + umf_result_t ret = umfPoolFree(hPool, ptr); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + size_t reserved_memory1 = 0; + ret = umfCtlGet("umf.pool.by_handle.{}.stats.reserved_memory", + &reserved_memory1, sizeof(size_t), hPool); + ASSERT_GE(reserved_memory1, 0ull); + + // if supported, call to umfPoolTrimMemory should purge the whole memory + // pool + ret = umfPoolTrimMemory(hPool, 0); + if (ret == UMF_RESULT_SUCCESS) { + size_t reserved_memory2 = 0; + ret = umfCtlGet("umf.pool.by_handle.{}.stats.reserved_memory", + &reserved_memory2, sizeof(size_t), hPool); + ASSERT_EQ(reserved_memory2, 0ull); + } +} + TEST_P(umfMemTest, outOfMem) { static constexpr size_t allocSize = 4096; auto hPool = pool.get(); diff --git a/test/pools/disjoint_pool.cpp b/test/pools/disjoint_pool.cpp index 92ccd0410..c638bfc3e 100644 --- a/test/pools/disjoint_pool.cpp +++ b/test/pools/disjoint_pool.cpp @@ -14,6 +14,7 @@ #include "provider.hpp" #include "provider_null.h" #include "provider_trace.h" +#include "umf/memory_provider.h" using umf_test::test; using namespace umf_test; @@ -274,6 +275,90 @@ TEST_F(test, sharedLimits) { EXPECT_EQ(MaxSize / SlabMinSize * 2, numFrees); } +TEST_F(test, disjointPoolTrim) { + struct memory_provider : public umf_test::provider_base_t { + umf_result_t alloc(size_t size, size_t alignment, void **ptr) noexcept { + *ptr = umf_ba_global_aligned_alloc(size, alignment); + return UMF_RESULT_SUCCESS; + } + + umf_result_t free(void *ptr, size_t) noexcept { + umf_ba_global_free(ptr); + return UMF_RESULT_SUCCESS; + } + }; + + umf_memory_provider_ops_t provider_ops = + umf_test::providerMakeCOps(); + + auto providerUnique = + wrapProviderUnique(createProviderChecked(&provider_ops, nullptr)); + + umf_memory_provider_handle_t provider_handle; + provider_handle = providerUnique.get(); + + umf_disjoint_pool_params_handle_t params = + (umf_disjoint_pool_params_handle_t)defaultDisjointPoolConfig(); + params->pool_trace = 3; + // Set the slab min size to 64 so allocating 64 bytes will use the whole + // slab. + params->slab_min_size = 64; + params->capacity = 4; + + // in "internals" test we use ops interface to directly manipulate the pool + // structure + const umf_memory_pool_ops_t *ops = umfDisjointPoolOps(); + EXPECT_NE(ops, nullptr); + + disjoint_pool_t *pool; + umf_result_t res = ops->initialize(provider_handle, params, (void **)&pool); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_NE(pool, nullptr); + + // do 4 allocs, then free all of them + size_t size = 64; + void *ptrs[4] = {0}; + ptrs[0] = ops->malloc(pool, size); + EXPECT_NE(ptrs[0], nullptr); + ptrs[1] = ops->malloc(pool, size); + EXPECT_NE(ptrs[1], nullptr); + ptrs[2] = ops->malloc(pool, size); + EXPECT_NE(ptrs[2], nullptr); + ptrs[3] = ops->malloc(pool, size); + EXPECT_NE(ptrs[3], nullptr); + + ops->free(pool, ptrs[0]); + ops->free(pool, ptrs[1]); + ops->free(pool, ptrs[2]); + ops->free(pool, ptrs[3]); + + // Because we set the slab min size to 64, each allocation should go to the + // separate slab. Additionally, because we set the capacity to 4, all slabs + // should still be in the pool available for new allocations. + EXPECT_EQ(pool->buckets[0]->available_slabs_num, (size_t)4); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_use, (size_t)0); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, (size_t)4); + + // Trim memory - leave 3 slabs in the pool + ops->ext_trim_memory(pool, 3 * pool->buckets[0]->size); + EXPECT_EQ(pool->buckets[0]->available_slabs_num, (size_t)3); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_use, (size_t)0); + + // Trim memory again - leave 1 slab in the pool + ops->ext_trim_memory(pool, pool->buckets[0]->size); + EXPECT_EQ(pool->buckets[0]->available_slabs_num, (size_t)1); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_use, (size_t)0); + + // Trim the rest of memory + ops->ext_trim_memory(pool, 0); + EXPECT_EQ(pool->buckets[0]->available_slabs_num, (size_t)0); + EXPECT_EQ(pool->buckets[0]->curr_slabs_in_pool, (size_t)0); + + ops->finalize(pool); + res = umfDisjointPoolParamsDestroy(params); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); +} + TEST_F(test, disjointPoolNullParams) { umf_result_t res = umfDisjointPoolParamsCreate(nullptr); EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); @@ -336,31 +421,56 @@ TEST_F(test, disjointPoolName) { umf_disjoint_pool_params_handle_t params = nullptr; umf_result_t res = umfDisjointPoolParamsCreate(¶ms); EXPECT_EQ(res, UMF_RESULT_SUCCESS); - umf_memory_provider_handle_t provider_handle = nullptr; umf_memory_pool_handle_t pool = NULL; - struct memory_provider : public umf_test::provider_base_t {}; + auto nullProvider = nullProviderCreate(); - umf_memory_provider_ops_t provider_ops = - umf_test::providerMakeCOps(); + res = umfPoolCreate(umfDisjointPoolOps(), nullProvider, params, 0, &pool); - auto providerUnique = - wrapProviderUnique(createProviderChecked(&provider_ops, nullptr)); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + const char *name = nullptr; + res = umfPoolGetName(pool, &name); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "disjoint"); - provider_handle = providerUnique.get(); + umfPoolDestroy(pool); + umfMemoryProviderDestroy(nullProvider); + umfDisjointPoolParamsDestroy(params); +} - res = - umfPoolCreate(umfDisjointPoolOps(), provider_handle, params, 0, &pool); +TEST_F(test, disjointPoolCustomName) { + umf_disjoint_pool_params_handle_t params = nullptr; + umf_result_t res = umfDisjointPoolParamsCreate(¶ms); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + + res = umfDisjointPoolParamsSetName(params, "my_disjoint"); EXPECT_EQ(res, UMF_RESULT_SUCCESS); + + struct memory_provider : public umf_test::provider_base_t {}; + + auto nullProvider = nullProviderCreate(); + umf_memory_pool_handle_t pool = NULL; + + res = umfPoolCreate(umfDisjointPoolOps(), nullProvider, params, 0, &pool); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + const char *name = nullptr; res = umfPoolGetName(pool, &name); EXPECT_EQ(res, UMF_RESULT_SUCCESS); - EXPECT_STREQ(name, "disjoint"); + EXPECT_STREQ(name, "my_disjoint"); umfPoolDestroy(pool); + umfMemoryProviderDestroy(nullProvider); umfDisjointPoolParamsDestroy(params); } +TEST(DisjointPoolOps, default_name_null_handle) { + const char *name = nullptr; + EXPECT_EQ(umfDisjointPoolOps()->get_name(nullptr, &name), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "disjoint"); +} + TEST_F(test, disjointPoolDefaultParams) { // Disjoint pool defaults static constexpr size_t DefaultSlabMinSize = 64 * 1024; // 64K @@ -499,7 +609,8 @@ INSTANTIATE_TEST_SUITE_P(disjointPoolTests, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfDisjointPoolOps(), defaultDisjointPoolConfig, defaultDisjointPoolConfigDestroy, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); void *memProviderParams() { return (void *)&DEFAULT_DISJOINT_CAPACITY; } @@ -510,10 +621,15 @@ INSTANTIATE_TEST_SUITE_P( defaultDisjointPoolConfigDestroy, &MOCK_OUT_OF_MEM_PROVIDER_OPS, memProviderParams, nullptr}, - static_cast(DEFAULT_DISJOINT_CAPACITY) / 2))); + static_cast(DEFAULT_DISJOINT_CAPACITY) / 2)), + ([](auto) { + return std::string("disjoint_out_of_mem_capacity_") + + std::to_string(static_cast(DEFAULT_DISJOINT_CAPACITY) / 2); + })); INSTANTIATE_TEST_SUITE_P(disjointMultiPoolTests, umfMultiPoolTest, ::testing::Values(poolCreateExtParams{ umfDisjointPoolOps(), defaultDisjointPoolConfig, defaultDisjointPoolConfigDestroy, - &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr})); + &BA_GLOBAL_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); diff --git a/test/pools/disjoint_pool_ctl.cpp b/test/pools/disjoint_pool_ctl.cpp index 5de142d32..37181ea4d 100644 --- a/test/pools/disjoint_pool_ctl.cpp +++ b/test/pools/disjoint_pool_ctl.cpp @@ -421,3 +421,249 @@ TEST_F(test, disjointCtlMemoryMetricsInvalidArgs) { ASSERT_SUCCESS(umfDisjointPoolParamsDestroy(params)); ASSERT_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); } + +TEST_F(test, disjointCtlBucketStats) { + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfOsMemoryProviderParamsCreate(&os_memory_provider_params)) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + ProviderWrapper providerWrapper(umfOsMemoryProviderOps(), + os_memory_provider_params); + if (providerWrapper.get() == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + umf_disjoint_pool_params_handle_t params = nullptr; + ASSERT_SUCCESS(umfDisjointPoolParamsCreate(¶ms)); + + // Set minimum slab size + size_t slab_min_size = 64 * 1024; + ASSERT_SUCCESS(umfDisjointPoolParamsSetSlabMinSize(params, slab_min_size)); + ASSERT_SUCCESS(umfDisjointPoolParamsSetCapacity(params, 4)); + ASSERT_SUCCESS(umfDisjointPoolParamsSetTrace(params, 3)); + + PoolWrapper poolWrapper(providerWrapper.get(), umfDisjointPoolOps(), + params); + + size_t arg = 0; + size_t count = 0; + const size_t alloc_size = 128; + size_t used_bucket = SIZE_MAX; + ASSERT_SUCCESS(umfCtlGet("umf.pool.by_handle.{}.buckets.count", &count, + sizeof(count), poolWrapper.get())); + EXPECT_GE(count, 0ull); + + auto expected_bucket_size = [](size_t i) -> size_t { + // Even indexes: 8 << (i/2) => 8,16,32,64,... + // Odd indexes: 12 << (i/2) => 12,24,48,96,... + return (i % 2 == 0) ? (size_t(8) << (i / 2)) : (size_t(12) << (i / 2)); + }; + + for (size_t i = 0; i < count; i++) { + ASSERT_SUCCESS(umfCtlGet("umf.pool.by_handle.{}.buckets.{}.size", &arg, + sizeof(arg), poolWrapper.get(), i)); + EXPECT_EQ(arg, expected_bucket_size(i)) << "Failed for bucket: " << i; + if (arg >= alloc_size && used_bucket == SIZE_MAX) { + used_bucket = i; // Find the bucket that matches alloc_size + } + } + + std::unordered_map stats = { + {"alloc_num", 0ull}, {"alloc_pool_num", 0ull}, + {"free_num", 0ull}, {"curr_slabs_in_use", 0ull}, + {"curr_slabs_in_pool", 0ull}, {"max_slabs_in_use", 0ull}, + {"max_slabs_in_pool", 0ull}, + }; + + for (const auto &s : stats) { + ASSERT_SUCCESS(umfCtlGet("umf.pool.by_handle.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), + s.first.c_str())); + EXPECT_EQ(arg, s.second) << "Failed for stat: " << s.first; + } + + for (size_t i = 0; i < count; i++) { + for (const auto &s : stats) { + ASSERT_SUCCESS( + umfCtlGet("umf.pool.by_handle.{}.buckets.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), i, s.first.c_str())); + EXPECT_EQ(arg, i == used_bucket ? s.second : 0) + << "Failed for stat: " << s.first << " bucket: " << i; + } + } + + const size_t n_allocations = 10; // Number of allocations + + // Allocate memory + std::vector ptrs; + for (size_t i = 0; i < n_allocations; i++) { + void *ptr = umfPoolMalloc(poolWrapper.get(), alloc_size); + ASSERT_NE(ptr, nullptr); + ptrs.push_back(ptr); + } + + stats = { + {"alloc_num", 10ull}, {"alloc_pool_num", 9ull}, + {"free_num", 0ull}, {"curr_slabs_in_use", 1ull}, + {"curr_slabs_in_pool", 0ull}, {"max_slabs_in_use", 1ull}, + {"max_slabs_in_pool", 0ull}, + }; + + for (const auto &s : stats) { + ASSERT_SUCCESS(umfCtlGet("umf.pool.by_handle.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), + s.first.c_str())); + EXPECT_EQ(arg, s.second) << "Failed for stat: " << s.first; + } + for (size_t i = 0; i < count; i++) { + for (const auto &s : stats) { + ASSERT_SUCCESS( + umfCtlGet("umf.pool.by_handle.{}.buckets.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), i, s.first.c_str())); + EXPECT_EQ(arg, i == used_bucket ? s.second : 0) + << "Failed for stat: " << s.first << " bucket: " << i; + } + } + + // Free all memory + for (void *ptr : ptrs) { + ASSERT_SUCCESS(umfPoolFree(poolWrapper.get(), ptr)); + } + + stats = { + {"alloc_num", 10ull}, {"alloc_pool_num", 9ull}, + {"free_num", 10ull}, {"curr_slabs_in_use", 0ull}, + {"curr_slabs_in_pool", 1ull}, {"max_slabs_in_use", 1ull}, + {"max_slabs_in_pool", 1ull}, + }; + + for (const auto &s : stats) { + ASSERT_SUCCESS(umfCtlGet("umf.pool.by_handle.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), + s.first.c_str())); + EXPECT_EQ(arg, s.second) << "Failed for stat: " << s.first; + } + + for (size_t i = 0; i < count; i++) { + for (const auto &s : stats) { + ASSERT_SUCCESS( + umfCtlGet("umf.pool.by_handle.{}.buckets.{}.stats.{}", &arg, + sizeof(arg), poolWrapper.get(), i, s.first.c_str())); + EXPECT_EQ(arg, i == used_bucket ? s.second : 0) + << "Failed for stat: " << s.first << " bucket: " << i; + } + } + + // Clean up + ASSERT_SUCCESS(umfDisjointPoolParamsDestroy(params)); + ASSERT_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); +} + +TEST_F(test, disjointCtlBucketStatsTraceDisabled) { + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfOsMemoryProviderParamsCreate(&os_memory_provider_params)) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + ProviderWrapper providerWrapper(umfOsMemoryProviderOps(), + os_memory_provider_params); + if (providerWrapper.get() == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + umf_disjoint_pool_params_handle_t params = nullptr; + ASSERT_SUCCESS(umfDisjointPoolParamsCreate(¶ms)); + + // Set minimum slab size + size_t slab_min_size = 64 * 1024; + ASSERT_SUCCESS(umfDisjointPoolParamsSetSlabMinSize(params, slab_min_size)); + ASSERT_SUCCESS(umfDisjointPoolParamsSetCapacity(params, 4)); + + PoolWrapper poolWrapper(providerWrapper.get(), umfDisjointPoolOps(), + params); + + size_t arg = 0; + // trace disabled + umf_result_t ret = umfCtlGet("umf.pool.by_handle.{}.stats.alloc_num", &arg, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_NOT_SUPPORTED); + + // Clean up + ASSERT_SUCCESS(umfDisjointPoolParamsDestroy(params)); + ASSERT_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); +} + +TEST_F(test, disjointCtlBucketStatsInvalid) { + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + if (UMF_RESULT_ERROR_NOT_SUPPORTED == + umfOsMemoryProviderParamsCreate(&os_memory_provider_params)) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + ProviderWrapper providerWrapper(umfOsMemoryProviderOps(), + os_memory_provider_params); + if (providerWrapper.get() == NULL) { + GTEST_SKIP() << "OS memory provider is not supported!"; + } + + umf_disjoint_pool_params_handle_t params = nullptr; + ASSERT_SUCCESS(umfDisjointPoolParamsCreate(¶ms)); + + // Set minimum slab size + size_t slab_min_size = 64 * 1024; + ASSERT_SUCCESS(umfDisjointPoolParamsSetSlabMinSize(params, slab_min_size)); + ASSERT_SUCCESS(umfDisjointPoolParamsSetCapacity(params, 4)); + ASSERT_SUCCESS(umfDisjointPoolParamsSetTrace(params, 3)); + PoolWrapper poolWrapper(providerWrapper.get(), umfDisjointPoolOps(), + params); + + size_t arg = 0; + + // invalid bucket index + umf_result_t ret = + umfCtlGet("umf.pool.by_handle.{}.buckets.1000000.stats.alloc_num", &arg, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // invalid arg + ret = umfCtlGet("umf.pool.by_handle.{}.stats.alloc_num", NULL, sizeof(arg), + poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfCtlGet("umf.pool.by_handle.{}.stats.alloc_num", &arg, 1, + poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfCtlGet("umf.pool.by_handle.{}.stats.buckets.count", NULL, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfCtlGet("umf.pool.by_handle.{}.stats.buckets.count", &arg, 1, + poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfCtlGet("umf.pool.by_handle.{}.stats.buckets.1.alloc_num", NULL, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + ret = umfCtlGet("umf.pool.by_handle.{}.stats.1.alloc_num", &arg, 1, + poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // no bucket id + ret = umfCtlGet("umf.pool.by_handle.{}.stats.buckets.alloc_num", &arg, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // bucked id + count + ret = umfCtlGet("umf.pool.by_handle.{}.stats.buckets.1.count", &arg, + sizeof(arg), poolWrapper.get()); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + // Clean up + ASSERT_SUCCESS(umfDisjointPoolParamsDestroy(params)); + ASSERT_SUCCESS(umfOsMemoryProviderParamsDestroy(os_memory_provider_params)); +} diff --git a/test/pools/jemalloc_coarse_devdax.cpp b/test/pools/jemalloc_coarse_devdax.cpp index 703f1176b..8af780997 100644 --- a/test/pools/jemalloc_coarse_devdax.cpp +++ b/test/pools/jemalloc_coarse_devdax.cpp @@ -10,7 +10,8 @@ bool devDaxEnvSet() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (path == nullptr || path[0] == 0 || size == nullptr || size[0] == 0) { + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { return false; } @@ -20,6 +21,10 @@ bool devDaxEnvSet() { void *createDevDaxParams() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { + return nullptr; + } umf_devdax_memory_provider_params_handle_t params = NULL; umf_result_t res = @@ -41,4 +46,5 @@ static std::vector poolParamsList = : std::vector{}; INSTANTIATE_TEST_SUITE_P(jemallocCoarseDevDaxTest, umfPoolTest, - ::testing::ValuesIn(poolParamsList)); + ::testing::ValuesIn(poolParamsList), + poolCreateExtParamsNameGen); diff --git a/test/pools/jemalloc_coarse_file.cpp b/test/pools/jemalloc_coarse_file.cpp index bce595a2b..b6f84804f 100644 --- a/test/pools/jemalloc_coarse_file.cpp +++ b/test/pools/jemalloc_coarse_file.cpp @@ -28,4 +28,5 @@ INSTANTIATE_TEST_SUITE_P(jemallocCoarseFileTest, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfJemallocPoolOps(), nullptr, nullptr, umfFileMemoryProviderOps(), getFileParamsDefault, - destroyFileParams})); + destroyFileParams}), + poolCreateExtParamsNameGen); diff --git a/test/pools/jemalloc_pool.cpp b/test/pools/jemalloc_pool.cpp index 906aba763..c87b049c1 100644 --- a/test/pools/jemalloc_pool.cpp +++ b/test/pools/jemalloc_pool.cpp @@ -87,7 +87,8 @@ INSTANTIATE_TEST_SUITE_P( poolCreateExtParams{umfJemallocPoolOps(), createJemallocParams<1>, destroyJemallocParams, umfOsMemoryProviderOps(), createOsMemoryProviderParams, - destroyOsMemoryProviderParams})); + destroyOsMemoryProviderParams}), + poolCreateExtParamsNameGen); // this test makes sure that jemalloc does not use // memory provider to allocate metadata (and hence @@ -225,3 +226,33 @@ TEST_F(test, jemallocProviderDoesNotSupportSplit) { umfMemoryProviderDestroy(ba_provider); umfJemallocPoolParamsDestroy(params); } + +TEST_F(test, jemallocPoolCustomName) { + umf_jemalloc_pool_params_handle_t params = nullptr; + umf_result_t res = umfJemallocPoolParamsCreate(¶ms); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + + res = umfJemallocPoolParamsSetName(params, "my_jemalloc"); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + + auto nullProvider = nullProviderCreate(); + + umf_memory_pool_handle_t pool = NULL; + res = umfPoolCreate(umfJemallocPoolOps(), nullProvider, params, 0, &pool); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + const char *name = nullptr; + res = umfPoolGetName(pool, &name); + EXPECT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "my_jemalloc"); + + umfPoolDestroy(pool); + umfMemoryProviderDestroy(nullProvider); + umfJemallocPoolParamsDestroy(params); +} + +TEST(JemallocPoolOps, default_name_null_handle) { + const char *name = nullptr; + EXPECT_EQ(umfJemallocPoolOps()->get_name(nullptr, &name), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "jemalloc"); +} diff --git a/test/pools/pool_base_alloc.cpp b/test/pools/pool_base_alloc.cpp index 4be438936..583b417cf 100644 --- a/test/pools/pool_base_alloc.cpp +++ b/test/pools/pool_base_alloc.cpp @@ -7,12 +7,11 @@ #include #include +#include "base_alloc_global.h" #include "pool.hpp" #include "poolFixtures.hpp" #include "provider.hpp" -#include "base_alloc_global.h" - struct base_alloc_pool : public umf_test::pool_base_t { void *malloc(size_t size) noexcept { return umf_ba_global_alloc(size); } @@ -50,6 +49,9 @@ struct base_alloc_pool : public umf_test::pool_base_t { } return UMF_RESULT_SUCCESS; } + umf_result_t ext_trim_memory(size_t) noexcept { + return UMF_RESULT_ERROR_NOT_SUPPORTED; + } }; umf_memory_pool_ops_t BA_POOL_OPS = @@ -58,4 +60,5 @@ umf_memory_pool_ops_t BA_POOL_OPS = INSTANTIATE_TEST_SUITE_P(baPool, umfPoolTest, ::testing::Values(poolCreateExtParams{ &BA_POOL_OPS, nullptr, nullptr, - &umf_test::BASE_PROVIDER_OPS, nullptr, nullptr})); + &umf_test::BASE_PROVIDER_OPS, nullptr, nullptr}), + poolCreateExtParamsNameGen); diff --git a/test/pools/scalable_coarse_devdax.cpp b/test/pools/scalable_coarse_devdax.cpp index 8dc8d576b..83fa85527 100644 --- a/test/pools/scalable_coarse_devdax.cpp +++ b/test/pools/scalable_coarse_devdax.cpp @@ -10,7 +10,8 @@ bool devDaxEnvSet() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (path == nullptr || path[0] == 0 || size == nullptr || size[0] == 0) { + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { return false; } @@ -20,6 +21,10 @@ bool devDaxEnvSet() { void *createDevDaxParams() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { + return nullptr; + } umf_devdax_memory_provider_params_handle_t params = NULL; umf_result_t res = @@ -45,4 +50,5 @@ static std::vector poolParamsList = : std::vector{}; INSTANTIATE_TEST_SUITE_P(scalableCoarseDevDaxTest, umfPoolTest, - ::testing::ValuesIn(poolParamsList)); + ::testing::ValuesIn(poolParamsList), + poolCreateExtParamsNameGen); diff --git a/test/pools/scalable_coarse_file.cpp b/test/pools/scalable_coarse_file.cpp index b9865b781..778a47202 100644 --- a/test/pools/scalable_coarse_file.cpp +++ b/test/pools/scalable_coarse_file.cpp @@ -28,4 +28,5 @@ INSTANTIATE_TEST_SUITE_P(scalableCoarseFileTest, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfScalablePoolOps(), nullptr, nullptr, umfFileMemoryProviderOps(), getFileParamsDefault, - destroyFileParams})); + destroyFileParams}), + poolCreateExtParamsNameGen); diff --git a/test/pools/scalable_pool.cpp b/test/pools/scalable_pool.cpp index 0c6830201..d34e16826 100644 --- a/test/pools/scalable_pool.cpp +++ b/test/pools/scalable_pool.cpp @@ -28,7 +28,8 @@ INSTANTIATE_TEST_SUITE_P( scalablePoolTest, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfScalablePoolOps(), nullptr, nullptr, umfOsMemoryProviderOps(), - createOsMemoryProviderParams, destroyOsMemoryProviderParams})); + createOsMemoryProviderParams, destroyOsMemoryProviderParams}), + poolCreateExtParamsNameGen); using scalablePoolParams = std::tuple; struct umfScalablePoolParamsTest @@ -165,7 +166,12 @@ INSTANTIATE_TEST_SUITE_P( scalablePoolTest, umfScalablePoolParamsTest, testing::Combine(testing::Values(2 * 1024 * 1024, 3 * 1024 * 1024, 4 * 1024 * 1024, 5 * 1024 * 1024), - testing::Values(false, true))); + testing::Values(false, true)), + ([](auto const &info) -> std::string { + return "scalable_granularity_" + + std::to_string(std::get<0>(info.param)) + "_keep_all_memory" + + (std::get<1>(info.param) ? "_true" : "_false"); + })); TEST(scalablePoolTest, scalablePoolName) { umf_memory_pool_handle_t pool = nullptr; @@ -189,3 +195,40 @@ TEST(scalablePoolTest, scalablePoolName) { umfMemoryProviderDestroy(provider); umfOsMemoryProviderParamsDestroy(provider_params); } + +TEST(scalablePoolTest, scalablePoolCustomName) { + umf_memory_pool_handle_t pool = nullptr; + umf_os_memory_provider_params_handle_t provider_params = nullptr; + umf_memory_provider_handle_t provider = nullptr; + + auto ret = umfOsMemoryProviderParamsCreate(&provider_params); + ret = umfMemoryProviderCreate(umfOsMemoryProviderOps(), provider_params, + &provider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_scalable_pool_params_handle_t params = nullptr; + ret = umfScalablePoolParamsCreate(¶ms); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_EQ(umfScalablePoolParamsSetName(params, "custom_scalable"), + UMF_RESULT_SUCCESS); + + ret = umfPoolCreate(umfScalablePoolOps(), provider, params, 0, &pool); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *name = nullptr; + ret = umfPoolGetName(pool, &name); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "custom_scalable"); + + umfPoolDestroy(pool); + umfScalablePoolParamsDestroy(params); + umfMemoryProviderDestroy(provider); + umfOsMemoryProviderParamsDestroy(provider_params); +} + +TEST(scalablePoolTest, default_name_null_handle) { + const char *name = nullptr; + EXPECT_EQ(umfScalablePoolOps()->get_name(nullptr, &name), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "scalable"); +} diff --git a/test/properties/provider_properties.cpp b/test/properties/provider_properties.cpp new file mode 100644 index 000000000..73542b528 --- /dev/null +++ b/test/properties/provider_properties.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include + +#include "memory_properties_internal.h" +#include "provider.hpp" +#include "provider_properties.hpp" + +void createFixedProvider(umf_memory_provider_handle_t *out_provider, + void *out_data) { + constexpr size_t buffer_size = 1024 * 1024; + + void *memory_buffer = malloc(buffer_size); + ASSERT_NE(memory_buffer, nullptr); + + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result_t res = + umfFixedMemoryProviderParamsCreate(memory_buffer, buffer_size, ¶ms); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + res = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, + out_provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(out_provider, nullptr); + + umfFixedMemoryProviderParamsDestroy(params); + + *(uintptr_t *)out_data = (uintptr_t)memory_buffer; +} + +void destroyFixedProvider(umf_memory_provider_handle_t provider, void *data) { + umfMemoryProviderDestroy(provider); + free(data); +} + +void createOsMemoryProvider(umf_memory_provider_handle_t *out_provider, + void *out_data) { + + umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; + umf_result_t res = + umfOsMemoryProviderParamsCreate(&os_memory_provider_params); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(os_memory_provider_params, nullptr); + + umf_memory_provider_handle_t os_memory_provider = nullptr; + res = + umfMemoryProviderCreate(umfOsMemoryProviderOps(), + os_memory_provider_params, &os_memory_provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(os_memory_provider, nullptr); + + res = umfOsMemoryProviderParamsDestroy(os_memory_provider_params); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + *out_provider = os_memory_provider; + *(uintptr_t *)out_data = (uintptr_t)NULL; +} + +void destroyOsMemoryProvider(umf_memory_provider_handle_t provider, + void *data) { + (void)data; // unused + + umfMemoryProviderDestroy(provider); +} + +INSTANTIATE_TEST_SUITE_P( + providerPropsTest, ProviderPropsTest, + ::testing::Values(testParams{createFixedProvider, destroyFixedProvider, + "fixedProvider"}, + testParams{createOsMemoryProvider, + destroyOsMemoryProvider, "osMemoryProvider"}), + nameGen); + +TEST_F(test, CustomPropsTest) { + const uint64_t custom_property_id = UMF_MEMORY_PROPERTY_MAX_RESERVED + 1; + + struct memory_provider : public umf_test::provider_base_t { + umf_result_t alloc(size_t size, size_t alignment, void **ptr) noexcept { + *ptr = umf_ba_global_aligned_alloc(size, alignment); + return UMF_RESULT_SUCCESS; + } + + umf_result_t free(void *ptr, [[maybe_unused]] size_t size) noexcept { + umf_ba_global_free(ptr); + return UMF_RESULT_SUCCESS; + } + + umf_result_t + get_min_page_size([[maybe_unused]] const void *ptr, + [[maybe_unused]] size_t *pageSize) noexcept { + *pageSize = 1024; + return UMF_RESULT_SUCCESS; + } + + umf_result_t ext_get_allocation_properties( + const void *ptr, umf_memory_property_id_t memory_property_id, + void *value) { + + (void)ptr; // unused + + if (memory_property_id == custom_property_id) { + *(uint64_t *)value = 42; // Custom value for the property + return UMF_RESULT_SUCCESS; + } + + return umf_test::provider_base_t::ext_get_allocation_properties( + ptr, memory_property_id, value); + } + + umf_result_t ext_get_allocation_properties_size( + umf_memory_property_id_t memory_property_id, size_t *size) { + if (memory_property_id == custom_property_id) { + *size = sizeof(uint64_t); + return UMF_RESULT_SUCCESS; + } + return umf_test::provider_base_t:: + ext_get_allocation_properties_size(memory_property_id, size); + } + }; + + umf_memory_provider_ops_t provider_ops = + umf_test::providerMakeCOps(); + + umf_memory_provider_handle_t provider = nullptr; + umf_result_t res = + umfMemoryProviderCreate(&provider_ops, nullptr, &provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + umf_memory_pool_handle_t pool = nullptr; + res = umfPoolCreate(umfProxyPoolOps(), provider, nullptr, + UMF_POOL_CREATE_FLAG_NONE, &pool); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(pool, nullptr); + + void *ptr = umfPoolMalloc(pool, 1024); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t properties = nullptr; + res = umfGetMemoryPropertiesHandle(ptr, &properties); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(properties, nullptr); + + // get value of the custom property from the properties handle + uint64_t value2 = 0; + res = umfGetMemoryProperty(properties, + (umf_memory_property_id_t)custom_property_id, + &value2, sizeof(value2)); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_EQ(value2, 42); + + res = umfPoolFree(pool, ptr); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + res = umfPoolDestroy(pool); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + + res = umfMemoryProviderDestroy(provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); +} diff --git a/test/properties/provider_properties.hpp b/test/properties/provider_properties.hpp new file mode 100644 index 000000000..2f51ad947 --- /dev/null +++ b/test/properties/provider_properties.hpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include +#include +#include +#include + +#include "base.hpp" +#include "test_helpers.h" + +using umf_test::test; + +using testParams = + std::tuple, + std::function, + const char *>; + +std::string nameGen(const testing::TestParamInfo param) { + return std::get<2>(param.param); +} + +struct ProviderPropsTest : umf_test::test, + ::testing::WithParamInterface { + void SetUp() override { + test::SetUp(); + + auto [create_fun, destroy_fun, name] = this->GetParam(); + provider_create = std::move(create_fun); + provider_destroy = std::move(destroy_fun); + (void)name; // unused + + provider_create(&provider, &data); + ASSERT_NE(provider, nullptr); + + umf_result_t umf_result = + umfPoolCreate(umfProxyPoolOps(), provider, nullptr, 0, &pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + } + + void TearDown() override { + umfPoolDestroy(pool); + provider_destroy(provider, data); + test::TearDown(); + } + + umf_memory_provider_handle_t provider; + umf_memory_pool_handle_t pool; + + std::function provider_create; + std::function provider_destroy; + void *data; +}; + +TEST_P(ProviderPropsTest, genericProps) { + umf_result_t umf_result; + const size_t alloc_size = 8; + + void *ptr = umfPoolMalloc(pool, alloc_size); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = nullptr; + umf_result = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + umf_memory_provider_handle_t param_provider = nullptr; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_PROVIDER_HANDLE, + ¶m_provider, sizeof(param_provider)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_EQ(param_provider, provider); + + umf_memory_pool_handle_t param_pool = nullptr; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_POOL_HANDLE, + ¶m_pool, sizeof(param_pool)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_EQ(param_pool, pool); + + void *base_address = nullptr; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &base_address, sizeof(base_address)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_EQ(base_address, ptr); + + size_t size = 0; + umf_result = umfGetMemoryProperty( + props_handle, UMF_MEMORY_PROPERTY_BASE_SIZE, &size, sizeof(size)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_EQ(size, alloc_size); + + uint64_t buffer_id = 0; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BUFFER_ID, + &buffer_id, sizeof(buffer_id)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_GE(buffer_id, 0); + + umf_result = umfPoolFree(pool, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +TEST_P(ProviderPropsTest, baseAddressFromMiddle) { + umf_result_t umf_result; + const size_t alloc_size = 8; + + void *ptr = umfPoolMalloc(pool, alloc_size); + ASSERT_NE(ptr, nullptr); + + void *ptr_mid = (void *)((uintptr_t)ptr + (alloc_size / 2)); + umf_memory_properties_handle_t props_handle = nullptr; + umf_result = umfGetMemoryPropertiesHandle(ptr_mid, &props_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + uintptr_t param_base_address = 0; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + ¶m_base_address, sizeof(param_base_address)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_EQ(param_base_address, (uintptr_t)ptr); + + umf_result = umfPoolFree(pool, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +TEST_P(ProviderPropsTest, uniqueBufferId) { + size_t alloc_size = 8; + size_t num_allocs = 10; + umf_result_t umf_result; + std::set buffer_ids; + + for (size_t i = 0; i < num_allocs; ++i) { + void *ptr = umfPoolMalloc(pool, alloc_size); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = nullptr; + umf_result = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + uint64_t buffer_id = 0; + umf_result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BUFFER_ID, + &buffer_id, sizeof(buffer_id)); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_GE(buffer_id, 0); + + // Ensure that the buffer ID is unique by inserting it into a set and + // checking if it was already present + ASSERT_TRUE(buffer_ids.find(buffer_id) == buffer_ids.end()); + ASSERT_TRUE(buffer_ids.insert(buffer_id).second); + + umf_result = umfPoolFree(pool, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + } +} + +// Negative tests + +TEST_P(ProviderPropsTest, invalidPointer) { + umf_memory_properties_handle_t props_handle = nullptr; + umf_result_t umf_result = + umfGetMemoryPropertiesHandle(nullptr, &props_handle); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + ASSERT_EQ(props_handle, nullptr); + + uintptr_t invalid_ptr = 0xdeadbeef; + umf_result = + umfGetMemoryPropertiesHandle((void *)invalid_ptr, &props_handle); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + ASSERT_EQ(props_handle, nullptr); +} + +TEST_P(ProviderPropsTest, invalidPropertyId) { + void *ptr = umfPoolMalloc(pool, 8); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = nullptr; + umf_result_t res = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + void *value = nullptr; + res = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_INVALID, + &value, sizeof(value)); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + res = umfPoolFree(pool, ptr); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); +} + +TEST_P(ProviderPropsTest, invalidPropertyValue) { + void *ptr = umfPoolMalloc(pool, 8); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = nullptr; + umf_result_t res = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + res = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + NULL, sizeof(int)); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + res = umfPoolFree(pool, ptr); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); +} + +TEST_P(ProviderPropsTest, invalidPropertySize) { + void *ptr = umfPoolMalloc(pool, 8); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = nullptr; + umf_result_t res = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + int value = 0; + res = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &value, size_t(0)); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + res = umfPoolFree(pool, ptr); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); +} + +TEST_P(ProviderPropsTest, nullPropertiesHandle) { + int val = 0; + umf_result_t res = umfGetMemoryProperty( + NULL, UMF_MEMORY_PROPERTY_BASE_ADDRESS, &val, sizeof(val)); + ASSERT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT); +} diff --git a/test/properties/provider_properties_cuda.cpp b/test/properties/provider_properties_cuda.cpp new file mode 100644 index 000000000..4ed5b8c02 --- /dev/null +++ b/test/properties/provider_properties_cuda.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "provider_properties.hpp" +#include "providers/cuda_helpers.h" + +void createCudaMemoryProvider(umf_memory_provider_handle_t *out_provider, + void *out_data) { + CUdevice hDevice = -1; + CUcontext hContext = NULL; + + int ret = init_cuda(); + ASSERT_EQ(ret, 0); + + ret = get_cuda_device(&hDevice); + ASSERT_EQ(ret, 0); + ASSERT_NE(hDevice, -1); + + ret = create_context(hDevice, &hContext); + ASSERT_EQ(ret, 0); + ASSERT_NE(hContext, nullptr); + + umf_cuda_memory_provider_params_handle_t cu_params = NULL; + umf_result_t umf_result = umfCUDAMemoryProviderParamsCreate(&cu_params); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(cu_params, nullptr); + + umf_result = umfCUDAMemoryProviderParamsSetContext(cu_params, hContext); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umf_result = umfCUDAMemoryProviderParamsSetDevice(cu_params, hDevice); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umf_result = umfCUDAMemoryProviderParamsSetMemoryType( + cu_params, UMF_MEMORY_TYPE_DEVICE); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t provider = nullptr; + umf_result = umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), cu_params, + &provider); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + umfCUDAMemoryProviderParamsDestroy(cu_params); + + *out_provider = provider; + *(uintptr_t *)out_data = (uintptr_t)hContext; +} + +void destroyCudaMemoryProvider(umf_memory_provider_handle_t provider, + void *data) { + destroy_context((CUcontext)data); + umfMemoryProviderDestroy(provider); +} + +INSTANTIATE_TEST_SUITE_P(providerPropsTest, ProviderPropsTest, + ::testing::Values(testParams{createCudaMemoryProvider, + destroyCudaMemoryProvider, + "cudaMemoryProvider"}), + nameGen); diff --git a/test/properties/provider_properties_level_zero.cpp b/test/properties/provider_properties_level_zero.cpp new file mode 100644 index 000000000..aa420cf50 --- /dev/null +++ b/test/properties/provider_properties_level_zero.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "provider_properties.hpp" +#include "utils/utils_level_zero.h" + +void levelZeroMemoryProviderCreate(umf_memory_provider_handle_t *out_provider, + void *out_data) { + + ze_driver_handle_t hDriver = nullptr; + ze_device_handle_t hDevice = nullptr; + ze_context_handle_t hContext = nullptr; + uint32_t driver_idx = 0; + + int ret = utils_ze_init_level_zero(); + ASSERT_EQ(ret, 0); + + ret = utils_ze_find_driver_with_gpu(&driver_idx, &hDriver); + ASSERT_EQ(ret, 0); + + ret = utils_ze_find_gpu_device(hDriver, &hDevice); + ASSERT_EQ(ret, 0); + + ret = utils_ze_create_context(hDriver, &hContext); + ASSERT_EQ(ret, 0); + + umf_level_zero_memory_provider_params_handle_t params = nullptr; + umf_result_t result = umfLevelZeroMemoryProviderParamsCreate(¶ms); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + result = umfLevelZeroMemoryProviderParamsSetContext(params, hContext); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + result = umfLevelZeroMemoryProviderParamsSetDevice(params, hDevice); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + result = umfLevelZeroMemoryProviderParamsSetMemoryType( + params, UMF_MEMORY_TYPE_DEVICE); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t provider = nullptr; + umf_result_t umf_result = umfMemoryProviderCreate( + umfLevelZeroMemoryProviderOps(), params, &provider); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + result = umfLevelZeroMemoryProviderParamsDestroy(params); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + + *out_provider = provider; + *(uintptr_t *)out_data = (uintptr_t)hContext; +} + +void levelZeroMemoryProviderDestroy(umf_memory_provider_handle_t provider, + void *data) { + umfMemoryProviderDestroy(provider); + utils_ze_destroy_context((ze_context_handle_t)data); +} + +INSTANTIATE_TEST_SUITE_P(providerPropsTest, ProviderPropsTest, + ::testing::Values(testParams{ + levelZeroMemoryProviderCreate, + levelZeroMemoryProviderDestroy, + "levelZeroProvider"}), + nameGen); diff --git a/test/provider_devdax_memory.cpp b/test/provider_devdax_memory.cpp index 31947fd3b..b22f4fa29 100644 --- a/test/provider_devdax_memory.cpp +++ b/test/provider_devdax_memory.cpp @@ -3,21 +3,23 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef _WIN32 -#include "test_helpers_linux.h" #include #include #include #endif -#include "base.hpp" - -#include "test_helpers.h" -#include "utils/cpp_helpers.hpp" - #include #include #include +#include "base.hpp" +#include "provider.hpp" +#include "test_helpers.h" +#include "utils/cpp_helpers.hpp" +#ifndef _WIN32 +#include "test_helpers_linux.h" +#endif + using umf_test::test; #define INVALID_PTR ((void *)0x01) @@ -44,23 +46,6 @@ static int compare_native_error_str(const char *message, int error) { return strncmp(message, error_str, len); } -using providerCreateExtParams = - std::tuple; - -static void providerCreateExt(providerCreateExtParams params, - umf_test::provider_unique_handle_t *handle) { - umf_memory_provider_handle_t hProvider = nullptr; - auto [provider_ops, provider_params] = params; - - auto ret = - umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); - ASSERT_NE(hProvider, nullptr); - - *handle = umf_test::provider_unique_handle_t(hProvider, - &umfMemoryProviderDestroy); -} - struct umfProviderTest : umf_test::test, ::testing::WithParamInterface { @@ -138,7 +123,7 @@ TEST_F(test, test_if_mapped_with_MAP_SYNC) { umf_result_t umf_result; char *path = getenv("UMF_TESTS_DEVDAX_PATH"); - if (path == nullptr || path[0] == 0) { + if (path == nullptr || path[0] == '\0') { GTEST_SKIP() << "Test skipped, UMF_TESTS_DEVDAX_PATH is not set"; } @@ -184,7 +169,8 @@ using devdax_params_unique_handle_t = devdax_params_unique_handle_t create_devdax_params() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (path == nullptr || path[0] == 0 || size == nullptr || size[0] == 0) { + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { return devdax_params_unique_handle_t( nullptr, &umfDevDaxMemoryProviderParamsDestroy); } @@ -212,7 +198,8 @@ static std::vector devdaxProviderTestParamsList = GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfProviderTest); INSTANTIATE_TEST_SUITE_P(devdaxProviderTest, umfProviderTest, - ::testing::ValuesIn(devdaxProviderTestParamsList)); + ::testing::ValuesIn(devdaxProviderTestParamsList), + providerCreateExtParamsNameGen); TEST_P(umfProviderTest, create_destroy) {} @@ -305,6 +292,36 @@ TEST_P(umfProviderTest, get_name) { ASSERT_STREQ(name, "DEVDAX"); } +TEST(DevDaxProviderName, custom_name) { + auto params_handle = create_devdax_params(); + if (!params_handle.get()) { + GTEST_SKIP() << "devdax params unavailable"; + } + + const char *custom = "my_devdax"; + auto ret = + umfDevDaxMemoryProviderParamsSetName(params_handle.get(), custom); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t prov = nullptr; + ret = umfMemoryProviderCreate(umfDevDaxMemoryProviderOps(), + params_handle.get(), &prov); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *name = nullptr; + ret = umfMemoryProviderGetName(prov, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); + umfMemoryProviderDestroy(prov); +} + +TEST(DevDaxProviderName, default_name_null_handle) { + const char *name = nullptr; + EXPECT_EQ(umfDevDaxMemoryProviderOps()->get_name(nullptr, &name), + UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "DEVDAX"); +} + TEST_P(umfProviderTest, free_size_0_ptr_not_null) { umf_result_t umf_result = umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); diff --git a/test/provider_devdax_memory_ipc.cpp b/test/provider_devdax_memory_ipc.cpp index d88b1f005..1db701153 100644 --- a/test/provider_devdax_memory_ipc.cpp +++ b/test/provider_devdax_memory_ipc.cpp @@ -18,7 +18,8 @@ using umf_test::test; bool devDaxEnvSet() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (path == nullptr || path[0] == 0 || size == nullptr || size[0] == 0) { + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { return false; } @@ -28,7 +29,8 @@ bool devDaxEnvSet() { void *defaultDevDaxParamsCreate() { char *path = getenv("UMF_TESTS_DEVDAX_PATH"); char *size = getenv("UMF_TESTS_DEVDAX_SIZE"); - if (path == nullptr || path[0] == 0 || size == nullptr || size[0] == 0) { + if (path == nullptr || path[0] == '\0' || size == nullptr || + size[0] == '\0') { return nullptr; } @@ -77,4 +79,5 @@ static std::vector getIpcProxyPoolTestParamsList(void) { GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfIpcTest); INSTANTIATE_TEST_SUITE_P(DevDaxProviderDifferentPoolsTest, umfIpcTest, - ::testing::ValuesIn(getIpcProxyPoolTestParamsList())); + ::testing::ValuesIn(getIpcProxyPoolTestParamsList()), + ipcTestParamsNameGen); diff --git a/test/provider_file_memory.cpp b/test/provider_file_memory.cpp index f79dac849..c59223eb5 100644 --- a/test/provider_file_memory.cpp +++ b/test/provider_file_memory.cpp @@ -2,18 +2,18 @@ // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "base.hpp" +#include +#include +#include +#include "base.hpp" +#include "provider.hpp" #include "test_helpers.h" #include "utils/cpp_helpers.hpp" #ifndef _WIN32 #include "test_helpers_linux.h" #endif -#include -#include -#include - using umf_test::test; #define FILE_PATH ((char *)"tmp_file") @@ -40,23 +40,6 @@ static int compare_native_error_str(const char *message, int error) { return strncmp(message, error_str, len); } -using providerCreateExtParams = - std::tuple; - -static void providerCreateExt(providerCreateExtParams params, - umf_test::provider_unique_handle_t *handle) { - umf_memory_provider_handle_t hProvider = nullptr; - auto [provider_ops, provider_params] = params; - - auto ret = - umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); - ASSERT_NE(hProvider, nullptr); - - *handle = umf_test::provider_unique_handle_t(hProvider, - &umfMemoryProviderDestroy); -} - struct FileProviderParamsDefault : umf_test::test, ::testing::WithParamInterface { @@ -136,7 +119,7 @@ TEST_F(test, test_if_mapped_with_MAP_SYNC) { umf_result_t umf_result; char *path = getenv("UMF_TESTS_FSDAX_PATH"); - if (path == nullptr || path[0] == 0) { + if (path == nullptr || path[0] == '\0') { GTEST_SKIP() << "Test skipped, UMF_TESTS_FSDAX_PATH is not set"; } @@ -219,7 +202,8 @@ file_params_unique_handle_t file_params_shared = INSTANTIATE_TEST_SUITE_P(fileProviderTest, FileProviderParamsDefault, ::testing::Values(providerCreateExtParams{ umfFileMemoryProviderOps(), - file_params_default.get()})); + file_params_default.get()}), + providerCreateExtParamsNameGen); TEST_P(FileProviderParamsDefault, create_destroy) {} @@ -370,6 +354,32 @@ TEST_P(FileProviderParamsDefault, get_name) { ASSERT_STREQ(name, "FILE"); } +TEST(FileProviderName, custom_name) { + auto params = get_file_params_default(FILE_PATH); + ASSERT_NE(params.get(), nullptr); + + const char *custom = "my_file"; + auto ret = umfFileMemoryProviderParamsSetName(params.get(), custom); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t prov = nullptr; + ret = umfMemoryProviderCreate(umfFileMemoryProviderOps(), params.get(), + &prov); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + const char *name = nullptr; + ret = umfMemoryProviderGetName(prov, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); + umfMemoryProviderDestroy(prov); +} + +TEST(FileProviderName, default_name_null_handle) { + const char *name = nullptr; + auto ret = umfFileMemoryProviderOps()->get_name(nullptr, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "FILE"); +} + TEST_P(FileProviderParamsDefault, free_size_0_ptr_not_null) { umf_result_t umf_result = umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); @@ -531,7 +541,8 @@ TEST_P(FileProviderParamsDefault, purge_force_INVALID_POINTER) { INSTANTIATE_TEST_SUITE_P(fileProviderTest, FileProviderParamsShared, ::testing::Values(providerCreateExtParams{ umfFileMemoryProviderOps(), - file_params_shared.get()})); + file_params_shared.get()}), + providerCreateExtParamsNameGen); TEST_P(FileProviderParamsShared, IPC_base_success_test) { umf_result_t umf_result; diff --git a/test/provider_file_memory_ipc.cpp b/test/provider_file_memory_ipc.cpp index b749772f4..fe50d8408 100644 --- a/test/provider_file_memory_ipc.cpp +++ b/test/provider_file_memory_ipc.cpp @@ -114,7 +114,7 @@ static std::vector getIpcFsDaxTestParamsList(void) { std::vector ipcFsDaxTestParamsList = {}; char *path = getenv("UMF_TESTS_FSDAX_PATH"); - if (path == nullptr || path[0] == 0) { + if (path == nullptr || path[0] == '\0') { // skipping the test, UMF_TESTS_FSDAX_PATH is not set return ipcFsDaxTestParamsList; } @@ -140,7 +140,9 @@ static std::vector getIpcFsDaxTestParamsList(void) { GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfIpcTest); INSTANTIATE_TEST_SUITE_P(FileProviderDifferentPoolsTest, umfIpcTest, - ::testing::ValuesIn(ipcManyPoolsTestParamsList)); + ::testing::ValuesIn(ipcManyPoolsTestParamsList), + ipcTestParamsNameGen); INSTANTIATE_TEST_SUITE_P(FileProviderDifferentPoolsFSDAXTest, umfIpcTest, - ::testing::ValuesIn(getIpcFsDaxTestParamsList())); + ::testing::ValuesIn(getIpcFsDaxTestParamsList()), + ipcTestParamsNameGen); diff --git a/test/provider_fixed_memory.cpp b/test/provider_fixed_memory.cpp index 59dcbb4d8..a72deda63 100644 --- a/test/provider_fixed_memory.cpp +++ b/test/provider_fixed_memory.cpp @@ -2,19 +2,19 @@ // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "base.hpp" +#include +#include +#include +#include +#include "base.hpp" +#include "provider.hpp" #include "test_helpers.h" #include "utils/cpp_helpers.hpp" #ifndef _WIN32 #include "test_helpers_linux.h" #endif -#include -#include -#include -#include - using umf_test::test; #define FIXED_BUFFER_SIZE (10 * utils_get_page_size()) @@ -39,23 +39,6 @@ static int compare_native_error_str(const char *message, int error) { return strncmp(message, error_str, len); } -using providerCreateExtParams = - std::tuple; - -static void providerCreateExt(providerCreateExtParams params, - umf_test::provider_unique_handle_t *handle) { - umf_memory_provider_handle_t hProvider = nullptr; - auto [provider_ops, provider_params] = params; - - auto ret = - umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); - ASSERT_NE(hProvider, nullptr); - - *handle = umf_test::provider_unique_handle_t(hProvider, - &umfMemoryProviderDestroy); -} - struct FixedProviderTest : umf_test::test, ::testing::WithParamInterface { @@ -155,7 +138,8 @@ struct FixedProviderTest INSTANTIATE_TEST_SUITE_P(fixedProviderTest, FixedProviderTest, ::testing::Values(providerCreateExtParams{ - umfFixedMemoryProviderOps(), nullptr})); + umfFixedMemoryProviderOps(), nullptr}), + providerCreateExtParamsNameGen); TEST_P(FixedProviderTest, create_destroy) { // Creation and destruction are handled in SetUp and TearDown @@ -279,6 +263,40 @@ TEST_P(FixedProviderTest, get_name) { ASSERT_STREQ(name, "FIXED"); } +TEST(FixedProviderName, custom_name) { + size_t mem_size = utils_get_page_size(); + void *buffer = malloc(mem_size); + ASSERT_NE(buffer, nullptr); + + umf_fixed_memory_provider_params_handle_t params = nullptr; + auto ret = umfFixedMemoryProviderParamsCreate(buffer, mem_size, ¶ms); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *custom = "my_fixed"; + ret = umfFixedMemoryProviderParamsSetName(params, custom); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t prov = nullptr; + ret = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, &prov); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *name = nullptr; + ret = umfMemoryProviderGetName(prov, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); + + umfMemoryProviderDestroy(prov); + umfFixedMemoryProviderParamsDestroy(params); + free(buffer); +} + +TEST(FixedProviderName, default_name_null_handle) { + const char *name = nullptr; + auto ret = umfFixedMemoryProviderOps()->get_name(nullptr, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "FIXED"); +} + TEST_P(FixedProviderTest, free_size_0_ptr_not_null) { umf_result_t umf_result = umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); diff --git a/test/provider_os_memory.cpp b/test/provider_os_memory.cpp index c18148d62..49b023a74 100644 --- a/test/provider_os_memory.cpp +++ b/test/provider_os_memory.cpp @@ -2,19 +2,19 @@ // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "base.hpp" - -#include "ipcFixtures.hpp" -#include "test_helpers.h" -#include "utils/cpp_helpers.hpp" - +#include #include #include #include #ifdef UMF_POOL_JEMALLOC_ENABLED #include #endif -#include + +#include "base.hpp" +#include "ipcFixtures.hpp" +#include "provider.hpp" +#include "test_helpers.h" +#include "utils/cpp_helpers.hpp" using umf_test::test; @@ -45,23 +45,6 @@ static int compare_native_error_str(const char *message, int error) { return strncmp(message, error_str, len); } -using providerCreateExtParams = - std::tuple; - -static void providerCreateExt(providerCreateExtParams params, - umf_test::provider_unique_handle_t *handle) { - umf_memory_provider_handle_t hProvider = nullptr; - auto [provider_ops, provider_params] = params; - - auto ret = - umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); - ASSERT_NE(hProvider, nullptr); - - *handle = umf_test::provider_unique_handle_t(hProvider, - &umfMemoryProviderDestroy); -} - struct umfProviderTest : umf_test::test, ::testing::WithParamInterface { @@ -219,9 +202,9 @@ TEST_F(test, create_ZERO_WEIGHT_PARTITION) { os_memory_provider_params, &p, 1); EXPECT_EQ(umf_result, UMF_RESULT_SUCCESS); - umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), - &os_memory_provider_params, - &os_memory_provider); + umf_result = + umfMemoryProviderCreate(umfOsMemoryProviderOps(), + os_memory_provider_params, &os_memory_provider); umfOsMemoryProviderParamsDestroy(os_memory_provider_params); @@ -248,7 +231,8 @@ auto defaultParams = createOsMemoryProviderParams(); INSTANTIATE_TEST_SUITE_P(osProviderTest, umfProviderTest, ::testing::Values(providerCreateExtParams{ - umfOsMemoryProviderOps(), defaultParams.get()})); + umfOsMemoryProviderOps(), defaultParams.get()}), + providerCreateExtParamsNameGen); TEST_P(umfProviderTest, create_destroy) {} @@ -335,6 +319,32 @@ TEST_P(umfProviderTest, get_name) { ASSERT_STREQ(name, "OS"); } +TEST(OsProviderName, custom_name) { + auto params = createOsMemoryProviderParams(); + ASSERT_NE(params.get(), nullptr); + const char *custom = "my_os"; + auto ret = umfOsMemoryProviderParamsSetName(params.get(), custom); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + umf_memory_provider_handle_t prov = nullptr; + ret = + umfMemoryProviderCreate(umfOsMemoryProviderOps(), params.get(), &prov); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + + const char *name = nullptr; + ret = umfMemoryProviderGetName(prov, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); + umfMemoryProviderDestroy(prov); +} + +TEST(OsProviderName, default_name_null_handle) { + const char *name = nullptr; + auto ret = umfOsMemoryProviderOps()->get_name(nullptr, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "OS"); +} + TEST_P(umfProviderTest, free_size_0_ptr_not_null) { umf_result_t umf_result = umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); @@ -557,4 +567,5 @@ static std::vector ipcTestParamsList = { }; INSTANTIATE_TEST_SUITE_P(osProviderTest, umfIpcTest, - ::testing::ValuesIn(ipcTestParamsList)); + ::testing::ValuesIn(ipcTestParamsList), + ipcTestParamsNameGen); diff --git a/test/provider_os_memory_config.cpp b/test/provider_os_memory_config.cpp index ed3456618..e5e2fdf97 100644 --- a/test/provider_os_memory_config.cpp +++ b/test/provider_os_memory_config.cpp @@ -1,20 +1,20 @@ /* * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * */ +#include +#include + #include "base.hpp" #include #include -#include -#include - static constexpr size_t allocSize = 4096; struct providerConfigTest : testing::Test { @@ -238,11 +238,17 @@ struct providerConfigTestNumaMode } }; -INSTANTIATE_TEST_SUITE_P(numa_modes, providerConfigTestNumaMode, - testing::Values(UMF_NUMA_MODE_DEFAULT, - UMF_NUMA_MODE_BIND, - UMF_NUMA_MODE_INTERLEAVE, - UMF_NUMA_MODE_LOCAL)); +INSTANTIATE_TEST_SUITE_P( + numa_modes, providerConfigTestNumaMode, + testing::Values(UMF_NUMA_MODE_DEFAULT, UMF_NUMA_MODE_BIND, + UMF_NUMA_MODE_INTERLEAVE, UMF_NUMA_MODE_LOCAL), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_NUMA_MODE_DEFAULT", "UMF_NUMA_MODE_BIND", + "UMF_NUMA_MODE_INTERLEAVE", "UMF_NUMA_MODE_LOCAL"}; + return names[info.index]; + })); + #ifndef MPOL_LOCAL #define MPOL_LOCAL 4 #endif diff --git a/test/provider_os_memory_multiple_numa_nodes.cpp b/test/provider_os_memory_multiple_numa_nodes.cpp index cfc58f2f0..69db6759f 100644 --- a/test/provider_os_memory_multiple_numa_nodes.cpp +++ b/test/provider_os_memory_multiple_numa_nodes.cpp @@ -1,11 +1,7 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "base.hpp" -#include "numa_helpers.hpp" -#include "test_helpers.h" - #include #include #include @@ -14,6 +10,10 @@ #include +#include "base.hpp" +#include "numa_helpers.hpp" +#include "test_helpers.h" + std::vector get_available_numa_nodes() { if (numa_available() == -1 || numa_all_nodes_ptr == nullptr) { return std::vector(); @@ -147,7 +147,10 @@ using the macro) GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(testNumaOnEachNode); INSTANTIATE_TEST_SUITE_P(testNumaNodesAllocations, testNumaOnEachNode, - ::testing::ValuesIn(get_available_numa_nodes())); + ::testing::ValuesIn(get_available_numa_nodes()), + ([](auto const &info) -> std::string { + return "numa_" + std::to_string(info.param); + })); // Test for allocations on numa nodes. It will be executed on each of // the available numa nodes. @@ -252,6 +255,7 @@ TEST_P(testNumaOnEachNode, checkModeInterleaveSingleNode) { constexpr int pages_num = 1024; size_t page_size = sysconf(_SC_PAGE_SIZE); + ASSERT_GT(page_size, 0); umf_result_t umf_result; umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; @@ -293,7 +297,10 @@ struct testNumaOnEachCpu : testNuma, testing::WithParamInterface { }; INSTANTIATE_TEST_SUITE_P(testNumaNodesAllocationsAllCpus, testNumaOnEachCpu, - ::testing::ValuesIn(get_available_cpus())); + ::testing::ValuesIn(get_available_cpus()), + ([](auto const &info) -> std::string { + return "cpu_" + std::to_string(info.param); + })); // Test for allocation on numa node with mode preferred and an empty nodeset. // For the empty nodeset the memory is allocated on the node of the CPU that @@ -421,6 +428,7 @@ TEST_F(testNuma, checkModeDefault) { TEST_F(testNuma, checkModeInterleave) { constexpr int pages_num = 1024; size_t page_size = sysconf(_SC_PAGE_SIZE); + ASSERT_GT(page_size, 0); umf_result_t umf_result; umf_os_memory_provider_params_handle_t os_memory_provider_params = nullptr; diff --git a/test/provider_os_memory_not_impl.cpp b/test/provider_os_memory_not_impl.cpp deleted file mode 100644 index 127ba32e4..000000000 --- a/test/provider_os_memory_not_impl.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2024-2025 Intel Corporation -// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include "base.hpp" - -#include - -using umf_test::test; - -TEST_F(test, os_provider_not_implemented) { - umf_os_memory_provider_params_handle_t params = nullptr; - umf_result_t umf_result = umfOsMemoryProviderParamsCreate(¶ms); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - EXPECT_EQ(params, nullptr); - - umf_result = umfOsMemoryProviderParamsDestroy(params); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = umfOsMemoryProviderParamsSetProtection(params, 0); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = - umfOsMemoryProviderParamsSetVisibility(params, UMF_MEM_MAP_PRIVATE); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = umfOsMemoryProviderParamsSetShmName(params, "shm_name"); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = umfOsMemoryProviderParamsSetNumaList(params, nullptr, 0); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = - umfOsMemoryProviderParamsSetNumaMode(params, UMF_NUMA_MODE_DEFAULT); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_result = umfOsMemoryProviderParamsSetPartSize(params, 4096); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - umf_numa_split_partition_t partitions[1]; - umf_result = umfOsMemoryProviderParamsSetPartitions(params, partitions, 1); - EXPECT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); - - const umf_memory_provider_ops_t *ops = umfOsMemoryProviderOps(); - EXPECT_EQ(ops, nullptr); -} diff --git a/test/provider_tracking.cpp b/test/provider_tracking.cpp index db186e15f..52d5eeb8f 100644 --- a/test/provider_tracking.cpp +++ b/test/provider_tracking.cpp @@ -2,40 +2,23 @@ // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "base.hpp" +#include +#include +#include +#include "base.hpp" +#include "provider.hpp" #include "test_helpers.h" #include "utils/cpp_helpers.hpp" #ifndef _WIN32 #include "test_helpers_linux.h" #endif -#include -#include -#include - using umf_test::test; #define FIXED_BUFFER_SIZE (512 * utils_get_page_size()) #define INVALID_PTR ((void *)0x01) -using providerCreateExtParams = - std::tuple; - -static void providerCreateExt(providerCreateExtParams params, - umf_test::provider_unique_handle_t *handle) { - umf_memory_provider_handle_t hProvider = nullptr; - auto [provider_ops, provider_params] = params; - - auto ret = - umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); - ASSERT_EQ(ret, UMF_RESULT_SUCCESS); - ASSERT_NE(hProvider, nullptr); - - *handle = umf_test::provider_unique_handle_t(hProvider, - &umfMemoryProviderDestroy); -} - struct TrackingProviderTest : umf_test::test, ::testing::WithParamInterface { @@ -121,7 +104,8 @@ createPoolFromAllocation(void *ptr0, size_t size1, INSTANTIATE_TEST_SUITE_P(trackingProviderTest, TrackingProviderTest, ::testing::Values(providerCreateExtParams{ - umfFixedMemoryProviderOps(), nullptr})); + umfFixedMemoryProviderOps(), nullptr}), + providerCreateExtParamsNameGen); TEST_P(TrackingProviderTest, create_destroy) { // Creation and destruction are handled in SetUp and TearDown diff --git a/test/provider_tracking_fixture_tests.cpp b/test/provider_tracking_fixture_tests.cpp index 5a26f7790..60ff9a176 100644 --- a/test/provider_tracking_fixture_tests.cpp +++ b/test/provider_tracking_fixture_tests.cpp @@ -92,10 +92,12 @@ INSTANTIATE_TEST_SUITE_P(TrackingProviderPoolTest, umfPoolTest, ::testing::Values(poolCreateExtParams{ umfProxyPoolOps(), nullptr, nullptr, &PROVIDER_FROM_POOL_OPS, - providerFromPoolParamsCreate, nullptr})); + providerFromPoolParamsCreate, nullptr}), + poolCreateExtParamsNameGen); INSTANTIATE_TEST_SUITE_P(TrackingProviderMultiPoolTest, umfMultiPoolTest, ::testing::Values(poolCreateExtParams{ umfProxyPoolOps(), nullptr, nullptr, &PROVIDER_FROM_POOL_OPS, - providerFromPoolParamsCreate, nullptr})); + providerFromPoolParamsCreate, nullptr}), + poolCreateExtParamsNameGen); diff --git a/test/providers/provider_cuda.cpp b/test/providers/provider_cuda.cpp index fd4160332..8a3d729eb 100644 --- a/test/providers/provider_cuda.cpp +++ b/test/providers/provider_cuda.cpp @@ -9,13 +9,14 @@ #include +#include +#include #include #include "cuda_helpers.h" #include "ipcFixtures.hpp" #include "pool.hpp" #include "utils_load_library.h" -#include using umf_test::test; using namespace umf_test; @@ -107,7 +108,7 @@ class CUDAMemoryAccessor : public MemoryAccessor { : hDevice_(hDevice), hContext_(hContext) {} void fill(void *ptr, size_t size, const void *pattern, - size_t pattern_size) { + size_t pattern_size) override { ASSERT_NE(hContext_, nullptr); ASSERT_GE(hDevice_, -1); ASSERT_NE(ptr, nullptr); @@ -117,7 +118,7 @@ class CUDAMemoryAccessor : public MemoryAccessor { ASSERT_EQ(ret, 0); } - void copy(void *dst_ptr, void *src_ptr, size_t size) { + void copy(void *dst_ptr, void *src_ptr, size_t size) override { ASSERT_NE(hContext_, nullptr); ASSERT_GE(hDevice_, -1); ASSERT_NE(dst_ptr, nullptr); @@ -127,6 +128,8 @@ class CUDAMemoryAccessor : public MemoryAccessor { ASSERT_EQ(ret, 0); } + const char *getName() override { return "CUDAMemoryAccessor"; } + private: CUdevice hDevice_; CUcontext hContext_; @@ -332,10 +335,34 @@ TEST_P(umfCUDAProviderTest, ctl_stats) { sizeof(peak), provider); ASSERT_EQ(ret, UMF_RESULT_SUCCESS); ASSERT_EQ(peak, 0u); + umfMemoryProviderDestroy(provider); +} + +TEST_P(umfCUDAProviderTest, custom_name) { + const char *custom = "my_cuda"; + ASSERT_EQ(umfCUDAMemoryProviderParamsSetName(params, custom), + UMF_RESULT_SUCCESS); + umf_memory_provider_handle_t provider = nullptr; + umf_result_t res = + umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + const char *name = nullptr; + res = umfMemoryProviderGetName(provider, &name); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); umfMemoryProviderDestroy(provider); } +TEST(umfCUDAProviderOps, default_name_null_handle) { + const char *name = nullptr; + auto ret = umfCUDAMemoryProviderOps()->get_name(nullptr, &name); + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "CUDA"); +} + TEST_P(umfCUDAProviderTest, allocInvalidSize) { CUcontext expected_current_context = get_current_context(); // create CUDA provider @@ -556,6 +583,87 @@ TEST_P(umfCUDAProviderTest, multiContext) { ASSERT_EQ(ret, 0); } +TEST_P(umfCUDAProviderTest, memProps) { + umf_memory_provider_handle_t provider = nullptr; + umf_result_t umf_result = + umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + umf_memory_pool_handle_t pool = NULL; + umf_result = umfPoolCreate(umfProxyPoolOps(), provider, NULL, 0, &pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + size_t size = 1024; + void *ptr = umfPoolMalloc(pool, size); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = NULL; + umf_result_t result = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + umf_usm_memory_type_t type = UMF_MEMORY_TYPE_UNKNOWN; + result = umfGetMemoryProperty( + props_handle, UMF_MEMORY_PROPERTY_POINTER_TYPE, &type, sizeof(type)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(type, expected_memory_type); + + void *baseAddress = nullptr; + result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &baseAddress, sizeof(baseAddress)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(baseAddress, ptr); + + size_t baseSize = 0; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_SIZE, + &baseSize, sizeof(baseSize)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_GE(baseSize, size); + + int64_t bufferId = 0; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BUFFER_ID, + &bufferId, sizeof(bufferId)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_GE(bufferId, 0); + + if (expected_memory_type != UMF_MEMORY_TYPE_HOST) { + CUdevice device = -1; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_DEVICE, + &device, sizeof(device)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(device, cudaTestHelper.get_test_device()); + } + + CUcontext context = nullptr; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_CONTEXT, + &context, sizeof(context)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(context, cudaTestHelper.get_test_context()); + + // check the props of pointer from the middle of alloc + void *midPtr = static_cast(ptr) + size / 2; + result = umfGetMemoryPropertiesHandle(midPtr, &props_handle); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + result = umfGetMemoryProperty( + props_handle, UMF_MEMORY_PROPERTY_POINTER_TYPE, &type, sizeof(type)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(type, expected_memory_type); + + result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &baseAddress, sizeof(baseAddress)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(baseAddress, ptr); + + umfFree(ptr); + + umfPoolDestroy(pool); + umfMemoryProviderDestroy(provider); +} + struct umfCUDAProviderAllocFlagsTest : umf_test::test, ::testing::WithParamInterface< @@ -668,9 +776,16 @@ TEST_P(umfCUDAProviderAllocFlagsTest, reuseParams) { // TODO add tests that mixes CUDA Memory Provider and Disjoint Pool INSTANTIATE_TEST_SUITE_P(umfCUDAProviderTestSuite, umfCUDAProviderTest, - ::testing::Values(UMF_MEMORY_TYPE_DEVICE, + ::testing::Values(UMF_MEMORY_TYPE_HOST, UMF_MEMORY_TYPE_SHARED, - UMF_MEMORY_TYPE_HOST)); + UMF_MEMORY_TYPE_DEVICE), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_MEMORY_TYPE_HOST", + "UMF_MEMORY_TYPE_SHARED", + "UMF_MEMORY_TYPE_DEVICE"}; + return names[info.index]; + })); INSTANTIATE_TEST_SUITE_P( umfCUDAProviderAllocFlagsTestSuite, umfCUDAProviderAllocFlagsTest, @@ -679,7 +794,13 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple(UMF_MEMORY_TYPE_SHARED, CU_MEM_ATTACH_HOST), std::make_tuple(UMF_MEMORY_TYPE_HOST, CU_MEMHOSTALLOC_PORTABLE), std::make_tuple(UMF_MEMORY_TYPE_HOST, CU_MEMHOSTALLOC_DEVICEMAP), - std::make_tuple(UMF_MEMORY_TYPE_HOST, CU_MEMHOSTALLOC_WRITECOMBINED))); + std::make_tuple(UMF_MEMORY_TYPE_HOST, CU_MEMHOSTALLOC_WRITECOMBINED)), + ([](auto const &info) -> std::string { + static const char *names[] = {"SHARED_GLOBAL", "SHARED_HOST", + "HOST_PORTABLE", "HOST_DEVICEMAP", + "HOST_WRITECOMBINED"}; + return names[info.index]; + })); // TODO: add IPC API GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfIpcTest); diff --git a/test/providers/provider_level_zero.cpp b/test/providers/provider_level_zero.cpp index d56b3dced..d91660399 100644 --- a/test/providers/provider_level_zero.cpp +++ b/test/providers/provider_level_zero.cpp @@ -9,13 +9,14 @@ #include +#include +#include #include #include "ipcFixtures.hpp" #include "pool.hpp" #include "utils_level_zero.h" #include "utils_load_library.h" -#include using umf_test::test; using namespace umf_test; @@ -114,7 +115,14 @@ struct LevelZeroProviderInit INSTANTIATE_TEST_SUITE_P(, LevelZeroProviderInit, ::testing::Values(UMF_MEMORY_TYPE_HOST, UMF_MEMORY_TYPE_DEVICE, - UMF_MEMORY_TYPE_SHARED)); + UMF_MEMORY_TYPE_SHARED), + ([](auto const &info) -> std::string { + static const char *names[] = { + "UMF_MEMORY_TYPE_HOST", + "UMF_MEMORY_TYPE_DEVICE", + "UMF_MEMORY_TYPE_SHARED"}; + return names[info.index]; + })); TEST_P(LevelZeroProviderInit, FailNullContext) { const umf_memory_provider_ops_t *ops = umfLevelZeroMemoryProviderOps(); @@ -217,7 +225,7 @@ class LevelZeroMemoryAccessor : public MemoryAccessor { ze_device_handle_t hDevice) : hDevice_(hDevice), hContext_(hContext) {} void fill(void *ptr, size_t size, const void *pattern, - size_t pattern_size) { + size_t pattern_size) override { ASSERT_NE(ptr, nullptr); int ret = utils_ze_level_zero_fill(hContext_, hDevice_, ptr, size, @@ -225,7 +233,7 @@ class LevelZeroMemoryAccessor : public MemoryAccessor { ASSERT_EQ(ret, 0); } - void copy(void *dst_ptr, void *src_ptr, size_t size) { + void copy(void *dst_ptr, void *src_ptr, size_t size) override { ASSERT_NE(dst_ptr, nullptr); ASSERT_NE(src_ptr, nullptr); @@ -234,6 +242,8 @@ class LevelZeroMemoryAccessor : public MemoryAccessor { ASSERT_EQ(ret, 0); } + const char *getName() override { return "LevelZeroMemoryAccessor"; } + private: ze_device_handle_t hDevice_; ze_context_handle_t hContext_; @@ -247,6 +257,7 @@ struct umfLevelZeroProviderTest test::SetUp(); umf_usm_memory_type_t memory_type = this->GetParam(); + umfExpectedMemoryType = memory_type; params = nullptr; memAccessor = nullptr; @@ -299,6 +310,7 @@ struct umfLevelZeroProviderTest std::unique_ptr memAccessor = nullptr; ze_context_handle_t hContext = nullptr; ze_memory_type_t zeMemoryTypeExpected = ZE_MEMORY_TYPE_UNKNOWN; + umf_usm_memory_type_t umfExpectedMemoryType = UMF_MEMORY_TYPE_UNKNOWN; }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfLevelZeroProviderTest); @@ -445,10 +457,35 @@ TEST_P(umfLevelZeroProviderTest, ctl_stats) { sizeof(peak), provider); ASSERT_EQ(ret, UMF_RESULT_SUCCESS); ASSERT_EQ(peak, 0u); + umfMemoryProviderDestroy(provider); +} + +TEST_P(umfLevelZeroProviderTest, custom_name) { + const char *custom = "my_level_zero"; + ASSERT_EQ(umfLevelZeroMemoryProviderParamsSetName(params, custom), + UMF_RESULT_SUCCESS); + umf_memory_provider_handle_t provider = nullptr; + umf_result_t res = umfMemoryProviderCreate(umfLevelZeroMemoryProviderOps(), + params, &provider); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + const char *name = nullptr; + res = umfMemoryProviderGetName(provider, &name); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, custom); umfMemoryProviderDestroy(provider); } +TEST(umfLevelZeroProviderOps, default_name_null_handle) { + const char *name = nullptr; + auto ret = umfLevelZeroMemoryProviderOps()->get_name(nullptr, &name); + + EXPECT_EQ(ret, UMF_RESULT_SUCCESS); + EXPECT_STREQ(name, "LEVEL_ZERO"); +} + TEST_P(umfLevelZeroProviderTest, allocInvalidSize) { umf_memory_provider_handle_t provider = nullptr; umf_result_t umf_result = umfMemoryProviderCreate( @@ -544,13 +581,100 @@ TEST_P(umfLevelZeroProviderTest, setDeviceOrdinalValid) { } } +TEST_P(umfLevelZeroProviderTest, memProps) { + umf_memory_provider_handle_t provider = nullptr; + umf_result_t umf_result = umfMemoryProviderCreate( + umfLevelZeroMemoryProviderOps(), params, &provider); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(provider, nullptr); + + umf_memory_pool_handle_t pool = NULL; + umf_result = umfPoolCreate(umfProxyPoolOps(), provider, NULL, 0, &pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + size_t size = 1024; + void *ptr = umfPoolMalloc(pool, size); + ASSERT_NE(ptr, nullptr); + + umf_memory_properties_handle_t props_handle = NULL; + umf_result_t result = umfGetMemoryPropertiesHandle(ptr, &props_handle); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + + umf_usm_memory_type_t type = UMF_MEMORY_TYPE_UNKNOWN; + result = umfGetMemoryProperty( + props_handle, UMF_MEMORY_PROPERTY_POINTER_TYPE, &type, sizeof(type)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(type, umfExpectedMemoryType); + + // base address and size + void *baseAddress = nullptr; + result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &baseAddress, sizeof(baseAddress)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(baseAddress, ptr); + + size_t baseSize = 0; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_SIZE, + &baseSize, sizeof(baseSize)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_GE(baseSize, size); + + int64_t bufferId = 0; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BUFFER_ID, + &bufferId, sizeof(bufferId)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_GE(bufferId, 0); + + if (umfExpectedMemoryType != UMF_MEMORY_TYPE_HOST) { + ze_device_handle_t device = nullptr; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_DEVICE, + &device, sizeof(device)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(device, l0TestHelper.get_test_device()); + } + + ze_context_handle_t context = nullptr; + result = umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_CONTEXT, + &context, sizeof(context)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(context, l0TestHelper.get_test_context()); + + // check the props of pointer from the middle of alloc + void *midPtr = static_cast(ptr) + size / 2; + result = umfGetMemoryPropertiesHandle(midPtr, &props_handle); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_NE(props_handle, nullptr); + result = umfGetMemoryProperty( + props_handle, UMF_MEMORY_PROPERTY_POINTER_TYPE, &type, sizeof(type)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(type, umfExpectedMemoryType); + + result = + umfGetMemoryProperty(props_handle, UMF_MEMORY_PROPERTY_BASE_ADDRESS, + &baseAddress, sizeof(baseAddress)); + ASSERT_EQ(result, UMF_RESULT_SUCCESS); + ASSERT_EQ(baseAddress, ptr); + + umfFree(ptr); + + umfPoolDestroy(pool); + umfMemoryProviderDestroy(provider); +} + // TODO add tests that mixes Level Zero Memory Provider and Disjoint Pool -INSTANTIATE_TEST_SUITE_P(umfLevelZeroProviderTestSuite, - umfLevelZeroProviderTest, - ::testing::Values(UMF_MEMORY_TYPE_DEVICE, - UMF_MEMORY_TYPE_SHARED, - UMF_MEMORY_TYPE_HOST)); +INSTANTIATE_TEST_SUITE_P( + umfLevelZeroProviderTestSuite, umfLevelZeroProviderTest, + ::testing::Values(UMF_MEMORY_TYPE_HOST, UMF_MEMORY_TYPE_SHARED, + UMF_MEMORY_TYPE_DEVICE), + ([](auto const &info) -> std::string { + static const char *names[] = {"UMF_MEMORY_TYPE_HOST", + "UMF_MEMORY_TYPE_SHARED", + "UMF_MEMORY_TYPE_DEVICE"}; + return names[info.index]; + })); LevelZeroTestHelper l0TestHelper; @@ -572,5 +696,6 @@ INSTANTIATE_TEST_SUITE_P( umfLevelZeroProviderTestSuite, umfIpcTest, ::testing::Values(ipcTestParams{ umfProxyPoolOps(), nullptr, nullptr, umfLevelZeroMemoryProviderOps(), - createL0ParamsDeviceMemory, destroyL0Params, &l0Accessor})); + createL0ParamsDeviceMemory, destroyL0Params, &l0Accessor}), + ipcTestParamsNameGen); #endif diff --git a/test/test_pool_null_params.cpp b/test/test_pool_null_params.cpp index b4eeae00d..0952e029a 100644 --- a/test/test_pool_null_params.cpp +++ b/test/test_pool_null_params.cpp @@ -55,7 +55,22 @@ const PoolOpsFn poolOpsList[] = { &umfProxyPoolOps #endif &umfDisjointPoolOps}; + +static const char *poolOpsNames[] = { +#if defined(UMF_POOL_SCALABLE_ENABLED) + "umfScalablePoolOps", +#endif +#if defined(UMF_POOL_JEMALLOC_ENABLED) + "umfJemallocPoolOps", +#endif +#if defined(UMF_POOL_PROXY_ENABLED) + "umfProxyPoolOps", +#endif + "umfDisjointPoolOps"}; } // namespace INSTANTIATE_TEST_SUITE_P(poolNullParamsTest, PoolNullParamsTest, - ::testing::ValuesIn(poolOpsList)); + ::testing::ValuesIn(poolOpsList), + ([](auto const &info) -> std::string { + return poolOpsNames[info.index]; + })); diff --git a/test/test_valgrind.sh b/test/test_valgrind.sh index cff45bdec..c877c4f44 100755 --- a/test/test_valgrind.sh +++ b/test/test_valgrind.sh @@ -128,6 +128,9 @@ for test in $TESTS; do echo "- SKIPPED" continue; # skip testing helper binaries used by the ipc_file_prov_* tests ;; + ./test/test_ctl_env_app) + continue; # this is not a standalone test + ;; ./test/test_memspace_host_all) FILTER='--gtest_filter="-*allocsSpreadAcrossAllNumaNodes"' ;; diff --git a/test/utils/cpp_helpers.hpp b/test/utils/cpp_helpers.hpp index ca1940e16..69a01a39f 100644 --- a/test/utils/cpp_helpers.hpp +++ b/test/utils/cpp_helpers.hpp @@ -10,11 +10,6 @@ #ifndef UMF_TEST_HELPERS_HPP #define UMF_TEST_HELPERS_HPP 1 -#include -#include -#include -#include - #include #include #include @@ -22,11 +17,17 @@ #include #include +#include +#include +#include +#include + namespace umf_test { using pool_unique_handle_t = std::unique_ptr>; + using provider_unique_handle_t = std::unique_ptr>; @@ -86,6 +87,8 @@ template umf_memory_pool_ops_t poolOpsBase() { UMF_ASSIGN_OP(ops, T, malloc_usable_size, UMF_RESULT_ERROR_UNKNOWN); UMF_ASSIGN_OP(ops, T, free, UMF_RESULT_ERROR_UNKNOWN); UMF_ASSIGN_OP(ops, T, get_last_allocation_error, UMF_RESULT_ERROR_UNKNOWN); + UMF_ASSIGN_OP(ops, T, ext_ctl, UMF_RESULT_ERROR_UNKNOWN); + UMF_ASSIGN_OP(ops, T, ext_trim_memory, UMF_RESULT_ERROR_UNKNOWN); return ops; } @@ -111,6 +114,10 @@ template constexpr umf_memory_provider_ops_t providerOpsBase() { UMF_ASSIGN_OP(ops, T, ext_put_ipc_handle, UMF_RESULT_ERROR_UNKNOWN); UMF_ASSIGN_OP(ops, T, ext_open_ipc_handle, UMF_RESULT_ERROR_UNKNOWN); UMF_ASSIGN_OP(ops, T, ext_close_ipc_handle, UMF_RESULT_ERROR_UNKNOWN); + UMF_ASSIGN_OP(ops, T, ext_get_allocation_properties, + UMF_RESULT_ERROR_UNKNOWN); + UMF_ASSIGN_OP(ops, T, ext_get_allocation_properties_size, + UMF_RESULT_ERROR_UNKNOWN); return ops; } } // namespace detail diff --git a/test/utils/utils_log.cpp b/test/utils/utils_log.cpp index cce61db58..4d1b0554e 100644 --- a/test/utils/utils_log.cpp +++ b/test/utils/utils_log.cpp @@ -1,8 +1,11 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include + #include "base.hpp" +#include "ctl/ctl_internal.h" #include "test_helpers.h" #define MOCK_FILE_PTR (FILE *)0xBADBEEF @@ -93,7 +96,7 @@ int mock_strerror_windows(char *buff, size_t s, int errnum) { } extern "C" { - +#define DISABLE_CTL_LOGGER 1 const char *env_variable = ""; #define fopen(A, B) mock_fopen(A, B) #define fputs(A, B) mock_fputs(A, B) @@ -135,13 +138,13 @@ void helper_checkConfig(utils_log_config_t *expected, utils_log_config_t *is) { EXPECT_EQ(expected->level, is->level); EXPECT_EQ(expected->flushLevel, is->flushLevel); EXPECT_EQ(expected->output, is->output); - EXPECT_EQ(expected->timestamp, is->timestamp); - EXPECT_EQ(expected->pid, is->pid); + EXPECT_EQ(expected->enableTimestamp, is->enableTimestamp); + EXPECT_EQ(expected->enablePid, is->enablePid); } TEST_F(test, parseEnv_errors) { expected_message = ""; - loggerConfig = {0, 0, LOG_ERROR, LOG_ERROR, NULL}; + loggerConfig = {false, false, LOG_ERROR, LOG_ERROR, NULL, ""}; expect_fput_count = 0; expected_stream = stderr; @@ -195,14 +198,16 @@ TEST_F(test, parseEnv) { {"output:file," + std::string(256, 'x'), MOCK_FILE_PTR}, {"output:file," + std::string(257, 'x'), NULL}, }; - std::vector> timestamps = { - {"timestamp:yes", 1}, - {"timestamp:invalid", 0}, - {"timestamp:no", 0}, - {"", 0}}; - - std::vector> pids = { - {"pid:yes", 1}, {"pid:invalid", 0}, {"pid:no", 0}, {"", 0}}; + std::vector> timestamps = { + {"timestamp:yes", true}, + {"timestamp:invalid", false}, + {"timestamp:no", false}, + {"", false}}; + + std::vector> pids = {{"pid:yes", true}, + {"pid:invalid", false}, + {"pid:no", false}, + {"", false}}; for (const auto &logLevel : logLevels) { for (const auto &flushLevel : flushLevels) { for (const auto &output : outputs) { @@ -212,7 +217,8 @@ TEST_F(test, parseEnv) { flushLevel.first + ";" + output.first + ";" + timestamp.first + ";" + pid.first; - b = loggerConfig = {0, 0, LOG_ERROR, LOG_ERROR, NULL}; + b = loggerConfig = {false, false, LOG_ERROR, + LOG_ERROR, NULL, ""}; expect_fput_count = 0; expect_fopen_count = 0; expected_stream = stderr; @@ -229,8 +235,8 @@ TEST_F(test, parseEnv) { expect_fopen_count = 1; } expected_stream = output.second; - b.timestamp = timestamp.second; - b.pid = pid.second; + b.enableTimestamp = timestamp.second; + b.enablePid = pid.second; b.flushLevel = (utils_log_level_t)flushLevel.second; b.level = (utils_log_level_t)logLevel.second; @@ -284,7 +290,8 @@ TEST_F(test, log_levels) { expected_stream = stderr; for (int i = LOG_DEBUG; i <= LOG_ERROR; i++) { for (int j = LOG_DEBUG; j <= LOG_ERROR; j++) { - loggerConfig = {0, 0, (utils_log_level_t)i, LOG_DEBUG, stderr}; + loggerConfig = {false, false, (utils_log_level_t)i, + LOG_DEBUG, stderr, ""}; if (i > j) { expect_fput_count = 0; expect_fflush_count = 0; @@ -307,7 +314,7 @@ TEST_F(test, log_outputs) { expect_fflush_count = 1; expected_message = "[DEBUG UMF] " + MOCK_FN_NAME + ": example log\n"; for (auto o : outs) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, o}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, o, ""}; expected_stream = o; helper_test_log(LOG_DEBUG, MOCK_FN_NAME.c_str(), "%s", "example log"); } @@ -318,7 +325,8 @@ TEST_F(test, flush_levels) { expect_fput_count = 1; for (int i = LOG_DEBUG; i <= LOG_ERROR; i++) { for (int j = LOG_DEBUG; j <= LOG_ERROR; j++) { - loggerConfig = {0, 0, LOG_DEBUG, (utils_log_level_t)i, stderr}; + loggerConfig = {false, false, LOG_DEBUG, (utils_log_level_t)i, + stderr, ""}; if (i > j) { expect_fflush_count = 0; } else { @@ -335,7 +343,7 @@ TEST_F(test, flush_levels) { TEST_F(test, long_log) { expect_fput_count = 1; expect_fflush_count = 1; - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_message = "[DEBUG UMF] " + MOCK_FN_NAME + ": " + std::string(8189 - MOCK_FN_NAME.size(), 'x') + "\n"; helper_test_log(LOG_DEBUG, MOCK_FN_NAME.c_str(), "%s", @@ -350,7 +358,7 @@ TEST_F(test, long_log) { TEST_F(test, timestamp_log) { expect_fput_count = 1; expect_fflush_count = 1; - loggerConfig = {1, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {true, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; // TODO: for now we do not check output message, // as it requires more sophisticated message validation (a.k.a regrex) expected_message = ""; @@ -360,7 +368,7 @@ TEST_F(test, timestamp_log) { TEST_F(test, pid_log) { expect_fput_count = 1; expect_fflush_count = 1; - loggerConfig = {0, 1, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, true, LOG_DEBUG, LOG_DEBUG, stderr, ""}; // TODO: for now we do not check output message, // as it requires more sophisticated message validation (a.k.a regrex) expected_message = ""; @@ -368,7 +376,7 @@ TEST_F(test, pid_log) { } TEST_F(test, log_fatal) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, NULL}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, NULL, ""}; expected_stream = stderr; expect_fput_count = 1; expect_fflush_count = 1; @@ -382,7 +390,7 @@ TEST_F(test, log_macros) { expected_stream = stderr; expect_fput_count = 1; expect_fflush_count = 1; - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_message = "[DEBUG UMF] TestBody: example log\n"; fput_count = 0; @@ -429,7 +437,7 @@ template void helper_test_plog(Args... args) { } TEST_F(test, plog_basic) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_stream = stderr; errno = 1; strerr = "test error"; @@ -445,7 +453,7 @@ TEST_F(test, plog_basic) { } TEST_F(test, plog_invalid) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_stream = stderr; errno = INVALID_ERRNO; strerr = "test error"; @@ -461,7 +469,7 @@ TEST_F(test, plog_invalid) { } TEST_F(test, plog_long_message) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_stream = stderr; expect_fput_count = 1; expect_fflush_count = 1; @@ -482,7 +490,7 @@ TEST_F(test, plog_long_message) { } TEST_F(test, plog_long_error) { - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; expected_stream = stderr; expect_fput_count = 1; expect_fflush_count = 1; @@ -508,7 +516,7 @@ TEST_F(test, log_pmacros) { expected_stream = stderr; expect_fput_count = 1; expect_fflush_count = 1; - loggerConfig = {0, 0, LOG_DEBUG, LOG_DEBUG, stderr}; + loggerConfig = {false, false, LOG_DEBUG, LOG_DEBUG, stderr, ""}; errno = 1; strerr = "test error"; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy